集成完直播后提交代码
1
LiveInteractive/live_barestream/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/build
|
||||
44
LiveInteractive/live_barestream/build.gradle
Normal file
@@ -0,0 +1,44 @@
|
||||
plugins {
|
||||
id 'com.android.library'
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion androidCompileSdkVersion
|
||||
buildToolsVersion androidBuildToolsVersion
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion androidMinSdkVersion
|
||||
targetSdkVersion androidTargetSdkVersion
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
|
||||
consumerProguardFiles "consumer-rules.pro"
|
||||
|
||||
javaCompileOptions {
|
||||
annotationProcessorOptions {
|
||||
arguments = [AROUTER_MODULE_NAME :'interactiveBareStream' ]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation externalAndroidDesign
|
||||
|
||||
implementation externalSimpleZXing
|
||||
|
||||
api project(':LiveInteractive:live_interactive_common')
|
||||
implementation externalARouter
|
||||
annotationProcessor externalARouterCompiler
|
||||
}
|
||||
0
LiveInteractive/live_barestream/consumer-rules.pro
Normal file
21
LiveInteractive/live_barestream/proguard-rules.pro
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
26
LiveInteractive/live_barestream/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.alivc.live.barestream_interactive" >
|
||||
|
||||
<application>
|
||||
<activity
|
||||
android:name=".InteractiveBareActivity"
|
||||
android:alwaysRetainTaskState="true"
|
||||
android:launchMode="singleTop"
|
||||
android:screenOrientation="portrait"
|
||||
android:theme="@style/Theme.AppCompat.Light.NoActionBar" />
|
||||
<activity
|
||||
android:name=".InteractiveMultiBareActivity"
|
||||
android:alwaysRetainTaskState="true"
|
||||
android:launchMode="singleTop"
|
||||
android:screenOrientation="portrait"
|
||||
android:theme="@style/Theme.AppCompat.Light.NoActionBar" />
|
||||
<activity
|
||||
android:name=".InteractiveInputURLActivity"
|
||||
android:alwaysRetainTaskState="true"
|
||||
android:launchMode="singleTop"
|
||||
android:screenOrientation="portrait"
|
||||
android:theme="@style/Theme.AppCompat.Light.NoActionBar" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@@ -0,0 +1,159 @@
|
||||
package com.alivc.live.barestream_interactive;
|
||||
|
||||
import android.content.Context;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import com.alivc.live.commonbiz.LocalStreamReader;
|
||||
import com.alivc.live.commonbiz.ResourcesConst;
|
||||
import com.alivc.live.interactive_common.InteractLiveBaseManager;
|
||||
import com.alivc.live.interactive_common.InteractiveMode;
|
||||
import com.alivc.live.interactive_common.bean.InteractiveUserData;
|
||||
import com.alivc.live.interactive_common.listener.InteractLivePushPullListener;
|
||||
import com.alivc.live.interactive_common.utils.LivePushGlobalConfig;
|
||||
import com.alivc.live.pusher.AlivcResolutionEnum;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
class BareStreamController {
|
||||
|
||||
private final InteractLiveBaseManager mInteractLiveManager;
|
||||
private final Context mContext;
|
||||
private final LocalStreamReader mLocalStreamReader;
|
||||
//主播预览 View
|
||||
private FrameLayout mAnchorRenderView;
|
||||
//观众连麦预览 View
|
||||
private FrameLayout mViewerRenderView;
|
||||
|
||||
// 主播连麦推流信息
|
||||
private InteractiveUserData mPushUserData;
|
||||
// 观众连麦拉流信息
|
||||
private InteractiveUserData mPullUserData;
|
||||
|
||||
public BareStreamController(Context context) {
|
||||
this.mContext = context;
|
||||
AlivcResolutionEnum resolution = LivePushGlobalConfig.mAlivcLivePushConfig.getResolution();
|
||||
int width = AlivcResolutionEnum.getResolutionWidth(resolution, LivePushGlobalConfig.mAlivcLivePushConfig.getLivePushMode());
|
||||
int height = AlivcResolutionEnum.getResolutionHeight(resolution, LivePushGlobalConfig.mAlivcLivePushConfig.getLivePushMode());
|
||||
mLocalStreamReader = new LocalStreamReader.Builder()
|
||||
.setVideoWith(width)
|
||||
.setVideoHeight(height)
|
||||
.setVideoStride(width)
|
||||
.setVideoSize(height * width * 3 / 2)
|
||||
.setVideoRotation(0)
|
||||
.setAudioSampleRate(44100)
|
||||
.setAudioChannel(1)
|
||||
.setAudioBufferSize(2048)
|
||||
.build();
|
||||
mInteractLiveManager = new InteractLiveBaseManager();
|
||||
mInteractLiveManager.init(context, InteractiveMode.BARE_STREAM);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置主播预览 View
|
||||
*
|
||||
* @param frameLayout 主播预览 View
|
||||
*/
|
||||
public void setAnchorRenderView(FrameLayout frameLayout) {
|
||||
this.mAnchorRenderView = frameLayout;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置观众预览 View
|
||||
*
|
||||
* @param frameLayout 观众预览 View
|
||||
*/
|
||||
public void setViewerRenderView(FrameLayout frameLayout) {
|
||||
this.mViewerRenderView = frameLayout;
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始直播
|
||||
*/
|
||||
public void startPush(InteractiveUserData userData) {
|
||||
mPushUserData = userData;
|
||||
externAV();
|
||||
mInteractLiveManager.startPreviewAndPush(mPushUserData, mAnchorRenderView, true);
|
||||
}
|
||||
|
||||
public void stopPreview() {
|
||||
mInteractLiveManager.stopPreview();
|
||||
}
|
||||
|
||||
public void stopPush() {
|
||||
mPushUserData = null;
|
||||
mInteractLiveManager.stopPush();
|
||||
}
|
||||
|
||||
public void stopCamera() {
|
||||
mInteractLiveManager.stopCamera();
|
||||
}
|
||||
|
||||
private void externAV() {
|
||||
if (LivePushGlobalConfig.mAlivcLivePushConfig.isExternMainStream()) {
|
||||
File yuvFile = ResourcesConst.localCaptureYUVFilePath(mContext);
|
||||
mLocalStreamReader.readYUVData(yuvFile, (buffer, pts, videoWidth, videoHeight, videoStride, videoSize, videoRotation) -> {
|
||||
mInteractLiveManager.inputStreamVideoData(buffer, videoWidth, videoHeight, videoStride, videoSize, pts, videoRotation);
|
||||
});
|
||||
File pcmFile = ResourcesConst.localCapturePCMFilePath(mContext);
|
||||
mLocalStreamReader.readPCMData(pcmFile, (buffer, length, pts, audioSampleRate, audioChannel) -> {
|
||||
mInteractLiveManager.inputStreamAudioData(buffer, length, audioSampleRate, audioChannel, pts);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 开始拉流
|
||||
public void startPull(InteractiveUserData userData) {
|
||||
mPullUserData = userData;
|
||||
mInteractLiveManager.setPullView(mPullUserData, mViewerRenderView, false);
|
||||
mInteractLiveManager.startPullRTCStream(mPullUserData);
|
||||
}
|
||||
|
||||
// 结束拉流
|
||||
public void stopPull() {
|
||||
mInteractLiveManager.stopPullRTCStream(mPullUserData);
|
||||
mPullUserData = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 主播是否正在连麦
|
||||
*
|
||||
* @return true:正在连麦 false:没有连麦
|
||||
*/
|
||||
public boolean isPulling() {
|
||||
return mInteractLiveManager.isPulling(mPullUserData);
|
||||
}
|
||||
|
||||
public void release() {
|
||||
mInteractLiveManager.release();
|
||||
mLocalStreamReader.stopYUV();
|
||||
mLocalStreamReader.stopPcm();
|
||||
}
|
||||
|
||||
public void setInteractLivePushPullListener(InteractLivePushPullListener listener) {
|
||||
mInteractLiveManager.setInteractLivePushPullListener(listener);
|
||||
}
|
||||
|
||||
public void switchCamera() {
|
||||
mInteractLiveManager.switchCamera();
|
||||
}
|
||||
|
||||
public void enableSpeakerPhone(boolean enable) {
|
||||
mInteractLiveManager.enableSpeakerPhone(enable);
|
||||
}
|
||||
|
||||
public void setMute(boolean b) {
|
||||
mInteractLiveManager.setMute(b);
|
||||
}
|
||||
|
||||
public void enableAudioCapture(boolean enable) {
|
||||
mInteractLiveManager.enableAudioCapture(enable);
|
||||
}
|
||||
|
||||
public void muteLocalCamera(boolean muteLocalCamera) {
|
||||
mInteractLiveManager.muteLocalCamera(muteLocalCamera);
|
||||
}
|
||||
|
||||
public void enableLocalCamera(boolean enable) {
|
||||
mInteractLiveManager.enableLocalCamera(enable);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,399 @@
|
||||
package com.alivc.live.barestream_interactive;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.Color;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.view.View;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.EditText;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.acker.simplezxing.activity.CaptureActivity;
|
||||
import com.alivc.live.commonui.utils.StatusBarUtil;
|
||||
import com.alivc.live.commonutils.ToastUtils;
|
||||
import com.alivc.live.interactive_common.bean.InteractiveUserData;
|
||||
import com.alivc.live.interactive_common.listener.InteractLivePushPullListener;
|
||||
import com.alivc.live.interactive_common.listener.InteractLiveTipsViewListener;
|
||||
import com.alivc.live.interactive_common.utils.InteractLiveIntent;
|
||||
import com.alivc.live.commonui.avdialog.AUILiveDialog;
|
||||
import com.alivc.live.interactive_common.widget.ConnectionLostTipsView;
|
||||
import com.alivc.live.interactive_common.widget.InteractiveCommonInputView;
|
||||
import com.alivc.live.interactive_common.widget.InteractiveConnectView;
|
||||
import com.alivc.live.interactive_common.widget.InteractiveRoomControlView;
|
||||
import com.alivc.live.player.annotations.AlivcLivePlayError;
|
||||
|
||||
public class InteractiveBareActivity extends AppCompatActivity {
|
||||
|
||||
private static final int REQ_CODE_PERMISSION = 0x1111;
|
||||
private static final int REQ_CODE_PUSH_URL = 0x2222;
|
||||
private static final int REQ_CODE_PULL_URL = 0x3333;
|
||||
public static final String DATA_INTERACTIVE_PUSH_URL = "data_interactive_push_url";
|
||||
public static final String DATA_INTERACTIVE_PULL_URL = "data_interactive_pull_url";
|
||||
|
||||
private AUILiveDialog mAUILiveDialog;
|
||||
|
||||
//Dialog 弹窗的意图
|
||||
private InteractLiveIntent mCurrentIntent;
|
||||
private ImageView mCloseImageView;
|
||||
private TextView mShowConnectIdTextView;
|
||||
//大窗口
|
||||
private FrameLayout mBigFrameLayout;
|
||||
//小窗口
|
||||
private FrameLayout mSmallFrameLayout;
|
||||
private ConnectionLostTipsView mConnectionLostTipsView;
|
||||
|
||||
//输入 URL 方式连麦
|
||||
private InteractiveUserData mPushUserData;
|
||||
private InteractiveUserData mPullUserData;
|
||||
|
||||
private InteractiveCommonInputView commonInputView;
|
||||
private InteractiveConnectView mInteractiveConnectView;
|
||||
private InteractiveRoomControlView mInteractiveRoomControlView;
|
||||
private BareStreamController mBareStreamController;
|
||||
private EditText mInteractivePushUrlEditText;
|
||||
private ImageView mPushUrlQrImageView;
|
||||
private TextView mStartPushTextView;
|
||||
private boolean mIsPushing;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
StatusBarUtil.translucent(this, Color.TRANSPARENT);
|
||||
setContentView(R.layout.activity_interactive_bare);
|
||||
|
||||
InteractiveUserData pushUserData = new InteractiveUserData();
|
||||
pushUserData.url = getIntent().getStringExtra(DATA_INTERACTIVE_PUSH_URL);
|
||||
mPushUserData = pushUserData;
|
||||
|
||||
InteractiveUserData pullUserData = new InteractiveUserData();
|
||||
pullUserData.url = getIntent().getStringExtra(DATA_INTERACTIVE_PULL_URL);
|
||||
mPullUserData = pullUserData;
|
||||
|
||||
mBareStreamController = new BareStreamController(this);
|
||||
|
||||
initView();
|
||||
initListener();
|
||||
initData();
|
||||
}
|
||||
|
||||
private void initData() {
|
||||
mInteractivePushUrlEditText.setText(mPushUserData != null ? mPushUserData.url : "");
|
||||
|
||||
mBareStreamController.setAnchorRenderView(mBigFrameLayout);
|
||||
mBareStreamController.setViewerRenderView(mSmallFrameLayout);
|
||||
}
|
||||
|
||||
private void initView() {
|
||||
mAUILiveDialog = new AUILiveDialog(this);
|
||||
mInteractiveConnectView = findViewById(R.id.connect_view);
|
||||
mCloseImageView = findViewById(R.id.iv_close);
|
||||
mShowConnectIdTextView = findViewById(R.id.tv_show_connect);
|
||||
mBigFrameLayout = findViewById(R.id.big_fl);
|
||||
mSmallFrameLayout = findViewById(R.id.small_fl);
|
||||
mInteractiveRoomControlView = findViewById(R.id.interactive_setting_view);
|
||||
|
||||
mConnectionLostTipsView = new ConnectionLostTipsView(this);
|
||||
|
||||
mPushUrlQrImageView = findViewById(R.id.iv_qr_push_url);
|
||||
mStartPushTextView = findViewById(R.id.tv_start_push);
|
||||
mInteractivePushUrlEditText = findViewById(R.id.et_interactive_push_url);
|
||||
|
||||
mInteractiveConnectView.setDefaultText(getResources().getString(R.string.interactive_bare_stream_start_pull));
|
||||
|
||||
}
|
||||
|
||||
private void initListener() {
|
||||
mPushUrlQrImageView.setOnClickListener(view -> {
|
||||
startQr(REQ_CODE_PUSH_URL);
|
||||
});
|
||||
|
||||
mStartPushTextView.setOnClickListener(view -> {
|
||||
if (mIsPushing) {
|
||||
mBareStreamController.stopPush();
|
||||
changePushState(false);
|
||||
} else {
|
||||
mPushUserData.url = mInteractivePushUrlEditText.getText().toString();
|
||||
mBareStreamController.startPush(mPushUserData);
|
||||
}
|
||||
});
|
||||
|
||||
mConnectionLostTipsView.setConnectionLostListener(() -> runOnUiThread(() -> finish()));
|
||||
|
||||
mBareStreamController.setInteractLivePushPullListener(new InteractLivePushPullListener() {
|
||||
@Override
|
||||
public void onPullSuccess(InteractiveUserData userData) {
|
||||
super.onPullSuccess(userData);
|
||||
changeSmallSurfaceViewVisible(true);
|
||||
updateConnectTextView(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPullError(InteractiveUserData userData, AlivcLivePlayError errorType, String errorMsg) {
|
||||
super.onPullError(userData, errorType, errorMsg);
|
||||
runOnUiThread(() -> {
|
||||
changeSmallSurfaceViewVisible(false);
|
||||
mBareStreamController.stopPull();
|
||||
updateConnectTextView(false);
|
||||
ToastUtils.show(getResources().getString(R.string.interact_live_viewer_left));
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPullStop(InteractiveUserData userData) {
|
||||
super.onPullStop(userData);
|
||||
runOnUiThread(() -> {
|
||||
changeSmallSurfaceViewVisible(false);
|
||||
updateConnectTextView(false);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPushSuccess() {
|
||||
super.onPushSuccess();
|
||||
changePushState(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPushError() {
|
||||
super.onPushError();
|
||||
changePushState(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectionLost() {
|
||||
super.onConnectionLost();
|
||||
runOnUiThread(() -> mConnectionLostTipsView.show());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceiveSEIMessage(int payload, byte[] data) {
|
||||
super.onReceiveSEIMessage(payload, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayerSei(int payload, byte[] uuid, byte[] data) {
|
||||
super.onPlayerSei(payload, uuid, data);
|
||||
}
|
||||
});
|
||||
|
||||
//开始连麦
|
||||
mInteractiveConnectView.setConnectClickListener(() -> {
|
||||
if (mBareStreamController.isPulling()) {
|
||||
//主播端停止连麦
|
||||
mCurrentIntent = InteractLiveIntent.INTENT_STOP_PULL;
|
||||
showInteractLiveDialog(getResources().getString(R.string.interact_live_connect_finish_tips), false);
|
||||
} else {
|
||||
//主播端开始连麦,输入用户 id
|
||||
if (mPullUserData == null || TextUtils.isEmpty(mPullUserData.url)) {
|
||||
showInteractLiveDialog(getResources().getString(R.string.interact_live_url_connect_tips), true);
|
||||
return;
|
||||
}
|
||||
mBareStreamController.startPull(mPullUserData);
|
||||
}
|
||||
});
|
||||
|
||||
mCloseImageView.setOnClickListener(view -> {
|
||||
mCurrentIntent = InteractLiveIntent.INTENT_FINISH;
|
||||
showInteractLiveDialog(getResources().getString(R.string.interact_live_leave_room_tips), false);
|
||||
});
|
||||
|
||||
mInteractiveRoomControlView.setOnClickEventListener(new InteractiveRoomControlView.OnClickEventListener() {
|
||||
@Override
|
||||
public void onClickSwitchCamera() {
|
||||
mBareStreamController.switchCamera();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClickSpeakerPhone(boolean enable) {
|
||||
mBareStreamController.enableSpeakerPhone(enable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClickMuteAudio(boolean mute) {
|
||||
mBareStreamController.setMute(mute);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClickMuteVideo(boolean mute) {
|
||||
mBareStreamController.setMute(mute);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClickEnableAudio(boolean enable) {
|
||||
mBareStreamController.enableAudioCapture(enable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClickEnableVideo(boolean enable) {
|
||||
mBareStreamController.enableLocalCamera(enable);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void changeSmallSurfaceViewVisible(boolean isShowSurfaceView) {
|
||||
mSmallFrameLayout.setVisibility(isShowSurfaceView ? View.VISIBLE : View.INVISIBLE);
|
||||
mInteractiveConnectView.isShow(isShowSurfaceView);
|
||||
}
|
||||
|
||||
public void updateConnectTextView(boolean connecting) {
|
||||
if (connecting) {
|
||||
mShowConnectIdTextView.setVisibility(View.VISIBLE);
|
||||
mInteractiveConnectView.connected(getResources().getString(R.string.interactive_bare_stream_stop_pull));
|
||||
} else {
|
||||
mShowConnectIdTextView.setVisibility(View.GONE);
|
||||
mInteractiveConnectView.unConnected(getResources().getString(R.string.interactive_bare_stream_start_pull));
|
||||
}
|
||||
}
|
||||
|
||||
private void showInteractLiveDialog(String content, boolean showInputView) {
|
||||
commonInputView = new InteractiveCommonInputView(InteractiveBareActivity.this);
|
||||
commonInputView.setViewType(InteractiveCommonInputView.ViewType.BARE_STREAM);
|
||||
commonInputView.showInputView(content, showInputView);
|
||||
mAUILiveDialog.setContentView(commonInputView);
|
||||
mAUILiveDialog.show();
|
||||
|
||||
commonInputView.setOnInteractLiveTipsViewListener(new InteractLiveTipsViewListener() {
|
||||
@Override
|
||||
public void onCancel() {
|
||||
if (mAUILiveDialog.isShowing()) {
|
||||
mAUILiveDialog.dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfirm() {
|
||||
if (mCurrentIntent == InteractLiveIntent.INTENT_STOP_PULL) {
|
||||
//主播结束连麦
|
||||
mAUILiveDialog.dismiss();
|
||||
mBareStreamController.stopPull();
|
||||
updateConnectTextView(false);
|
||||
changeSmallSurfaceViewVisible(false);
|
||||
} else if (mCurrentIntent == InteractLiveIntent.INTENT_FINISH) {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInputConfirm(InteractiveUserData userData) {
|
||||
hideInputSoftFromWindowMethod(InteractiveBareActivity.this, commonInputView);
|
||||
if (userData == null || TextUtils.isEmpty(userData.url)) {
|
||||
ToastUtils.show(getResources().getString(R.string.interact_live_connect_input_error_tips));
|
||||
return;
|
||||
}
|
||||
mAUILiveDialog.dismiss();
|
||||
// 主播端,输入观众 id 后,开始连麦
|
||||
mPullUserData = userData;
|
||||
mBareStreamController.startPull(userData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onQrClick() {
|
||||
startQr(REQ_CODE_PULL_URL);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
mBareStreamController.release();
|
||||
}
|
||||
|
||||
public void hideInputSoftFromWindowMethod(Context context, View view) {
|
||||
try {
|
||||
InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
switch (requestCode) {
|
||||
case REQ_CODE_PUSH_URL:
|
||||
case REQ_CODE_PULL_URL:
|
||||
switch (resultCode) {
|
||||
case RESULT_OK:
|
||||
if (requestCode == REQ_CODE_PUSH_URL) {
|
||||
if (mInteractivePushUrlEditText != null) {
|
||||
mInteractivePushUrlEditText.setText(data.getStringExtra(CaptureActivity.EXTRA_SCAN_RESULT));
|
||||
}
|
||||
} else {
|
||||
if (commonInputView != null) {
|
||||
commonInputView.setQrResult(data.getStringExtra(CaptureActivity.EXTRA_SCAN_RESULT));
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case RESULT_CANCELED:
|
||||
if (data != null) {
|
||||
// for some reason camera is not working correctly
|
||||
ToastUtils.show(getString(R.string.live_push_switch_to_facing_back_camera));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void changePushState(boolean isSuccess) {
|
||||
this.mIsPushing = isSuccess;
|
||||
if (isSuccess) {
|
||||
mStartPushTextView.setText(getResources().getString(com.alivc.live.interactive_common.R.string.interact_stop_push));
|
||||
mStartPushTextView.setBackground(getResources().getDrawable(com.alivc.live.interactive_common.R.drawable.shape_interact_live_un_connect_btn_bg));
|
||||
} else {
|
||||
mStartPushTextView.setText(getResources().getString(com.alivc.live.interactive_common.R.string.interact_start_push));
|
||||
mStartPushTextView.setBackground(getResources().getDrawable(com.alivc.live.interactive_common.R.drawable.shape_pysh_btn_bg));
|
||||
}
|
||||
}
|
||||
|
||||
private void startQr(int requestCode) {
|
||||
if (ContextCompat.checkSelfPermission(InteractiveBareActivity.this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
|
||||
// Do not have the permission of camera, request it.
|
||||
ActivityCompat.requestPermissions(InteractiveBareActivity.this, new String[]{Manifest.permission.CAMERA}, REQ_CODE_PERMISSION);
|
||||
} else {
|
||||
// Have gotten the permission
|
||||
startCaptureActivityForResult(requestCode);
|
||||
}
|
||||
}
|
||||
|
||||
private void startCaptureActivityForResult(int requestCode) {
|
||||
Intent intent = new Intent(InteractiveBareActivity.this, CaptureActivity.class);
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putBoolean(CaptureActivity.KEY_NEED_BEEP, CaptureActivity.VALUE_BEEP);
|
||||
bundle.putBoolean(CaptureActivity.KEY_NEED_VIBRATION, CaptureActivity.VALUE_VIBRATION);
|
||||
bundle.putBoolean(CaptureActivity.KEY_NEED_EXPOSURE, CaptureActivity.VALUE_NO_EXPOSURE);
|
||||
bundle.putByte(CaptureActivity.KEY_FLASHLIGHT_MODE, CaptureActivity.VALUE_FLASHLIGHT_OFF);
|
||||
bundle.putByte(CaptureActivity.KEY_ORIENTATION_MODE, CaptureActivity.VALUE_ORIENTATION_AUTO);
|
||||
bundle.putBoolean(CaptureActivity.KEY_SCAN_AREA_FULL_SCREEN, CaptureActivity.VALUE_SCAN_AREA_FULL_SCREEN);
|
||||
bundle.putBoolean(CaptureActivity.KEY_NEED_SCAN_HINT_TEXT, CaptureActivity.VALUE_SCAN_HINT_TEXT);
|
||||
intent.putExtra(CaptureActivity.EXTRA_SETTING_BUNDLE, bundle);
|
||||
startActivityForResult(intent, requestCode);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
package com.alivc.live.barestream_interactive;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.Color;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.acker.simplezxing.activity.CaptureActivity;
|
||||
import com.alivc.live.commonui.utils.StatusBarUtil;
|
||||
import com.alivc.live.interactive_common.InteractiveSettingActivity;
|
||||
import com.alivc.live.interactive_common.utils.LivePushGlobalConfig;
|
||||
import com.alivc.live.interactive_common.widget.InteractiveQrEditView;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
public class InteractiveInputURLActivity extends AppCompatActivity {
|
||||
|
||||
private static final int REQ_CODE_PERMISSION = 0x1111;
|
||||
private static final int REQ_CODE_PUSH_URL = 0x2222;
|
||||
private static final int REQ_CODE_PULL_URL = 0x3333;
|
||||
private static final int REQ_CODE_PULL_URL_1 = 0x4444;
|
||||
private static final int REQ_CODE_PULL_URL_2 = 0x5555;
|
||||
private static final int REQ_CODE_PULL_URL_3 = 0x6666;
|
||||
|
||||
private TextView mConfirmTextView;
|
||||
private TextView mSettingTextView;
|
||||
private ImageView mBackImageView;
|
||||
private InteractiveQrEditView mInteractivePushView;
|
||||
private InteractiveQrEditView mInteractivePullView;
|
||||
private InteractiveQrEditView mInteractivePullView1;
|
||||
private InteractiveQrEditView mInteractivePullView2;
|
||||
private InteractiveQrEditView mInteractivePullView3;
|
||||
private LinearLayout mMorePullViewRoot;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
StatusBarUtil.translucent(this, Color.TRANSPARENT);
|
||||
|
||||
setContentView(R.layout.activity_interactive_input_url);
|
||||
|
||||
initView();
|
||||
initListener();
|
||||
}
|
||||
|
||||
private void initView() {
|
||||
mBackImageView = findViewById(R.id.iv_back);
|
||||
mConfirmTextView = findViewById(R.id.tv_confirm);
|
||||
mSettingTextView = findViewById(R.id.tv_setting);
|
||||
|
||||
mMorePullViewRoot = findViewById(R.id.more_pull_view);
|
||||
mInteractivePushView = findViewById(R.id.push_view);
|
||||
mInteractivePullView = findViewById(R.id.pull_view);
|
||||
mInteractivePullView1 = findViewById(R.id.pull_view1);
|
||||
mInteractivePullView2 = findViewById(R.id.pull_view2);
|
||||
mInteractivePullView3 = findViewById(R.id.pull_view3);
|
||||
}
|
||||
|
||||
private void initListener() {
|
||||
mInteractivePushView.setOnQrClickListener(() -> {
|
||||
startQr(REQ_CODE_PUSH_URL);
|
||||
});
|
||||
mInteractivePullView.setOnQrClickListener(() -> {
|
||||
startQr(REQ_CODE_PULL_URL);
|
||||
});
|
||||
|
||||
mInteractivePullView1.setOnQrClickListener(() -> {
|
||||
startQr(REQ_CODE_PULL_URL_1);
|
||||
});
|
||||
|
||||
mInteractivePullView2.setOnQrClickListener(() -> {
|
||||
startQr(REQ_CODE_PULL_URL_2);
|
||||
});
|
||||
|
||||
mInteractivePullView3.setOnQrClickListener(() -> {
|
||||
startQr(REQ_CODE_PULL_URL_3);
|
||||
});
|
||||
|
||||
mBackImageView.setOnClickListener(view -> finish());
|
||||
|
||||
mConfirmTextView.setOnClickListener(view -> {
|
||||
HashMap<String, String> extras = new HashMap<>();
|
||||
|
||||
String pushUrl = mInteractivePushView.getEditText();
|
||||
String pullUrl = mInteractivePullView.getEditText();
|
||||
extras.put(InteractiveBareActivity.DATA_INTERACTIVE_PUSH_URL, pushUrl);
|
||||
extras.put(InteractiveBareActivity.DATA_INTERACTIVE_PULL_URL, pullUrl);
|
||||
|
||||
Intent intent;
|
||||
if (LivePushGlobalConfig.IS_MULTI_INTERACT) {
|
||||
intent = new Intent(InteractiveInputURLActivity.this, InteractiveMultiBareActivity.class);
|
||||
|
||||
String pullUrl1 = mInteractivePullView1.getEditText();
|
||||
String pullUrl2 = mInteractivePullView2.getEditText();
|
||||
String pullUrl3 = mInteractivePullView3.getEditText();
|
||||
extras.put(InteractiveMultiBareActivity.DATA_INTERACTIVE_PULL_URL_1, pullUrl1);
|
||||
extras.put(InteractiveMultiBareActivity.DATA_INTERACTIVE_PULL_URL_2, pullUrl2);
|
||||
extras.put(InteractiveMultiBareActivity.DATA_INTERACTIVE_PULL_URL_3, pullUrl3);
|
||||
} else {
|
||||
intent = new Intent(InteractiveInputURLActivity.this, InteractiveBareActivity.class);
|
||||
}
|
||||
|
||||
for (HashMap.Entry<String, String> entry : extras.entrySet()) {
|
||||
intent.putExtra(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
startActivity(intent);
|
||||
});
|
||||
|
||||
mSettingTextView.setOnClickListener(view -> {
|
||||
Intent intent = new Intent(this, InteractiveSettingActivity.class);
|
||||
startActivity(intent);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
mMorePullViewRoot.setVisibility(LivePushGlobalConfig.IS_MULTI_INTERACT ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
switch (requestCode) {
|
||||
case REQ_CODE_PUSH_URL:
|
||||
case REQ_CODE_PULL_URL:
|
||||
case REQ_CODE_PULL_URL_1:
|
||||
case REQ_CODE_PULL_URL_2:
|
||||
case REQ_CODE_PULL_URL_3:
|
||||
switch (resultCode) {
|
||||
case RESULT_OK:
|
||||
if (requestCode == REQ_CODE_PUSH_URL) {
|
||||
if (mInteractivePushView != null) {
|
||||
mInteractivePushView.setEditText(data.getStringExtra(CaptureActivity.EXTRA_SCAN_RESULT));
|
||||
}
|
||||
} else if (requestCode == REQ_CODE_PULL_URL) {
|
||||
if (mInteractivePullView != null) {
|
||||
mInteractivePullView.setEditText(data.getStringExtra(CaptureActivity.EXTRA_SCAN_RESULT));
|
||||
}
|
||||
} else if (requestCode == REQ_CODE_PULL_URL_1) {
|
||||
if (mInteractivePullView1 != null) {
|
||||
mInteractivePullView1.setEditText(data.getStringExtra(CaptureActivity.EXTRA_SCAN_RESULT));
|
||||
}
|
||||
} else if (requestCode == REQ_CODE_PULL_URL_2) {
|
||||
if (mInteractivePullView2 != null) {
|
||||
mInteractivePullView2.setEditText(data.getStringExtra(CaptureActivity.EXTRA_SCAN_RESULT));
|
||||
}
|
||||
} else {
|
||||
if (mInteractivePullView3 != null) {
|
||||
mInteractivePullView3.setEditText(data.getStringExtra(CaptureActivity.EXTRA_SCAN_RESULT));
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void startQr(int requestCode) {
|
||||
if (ContextCompat.checkSelfPermission(InteractiveInputURLActivity.this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
|
||||
// Do not have the permission of camera, request it.
|
||||
ActivityCompat.requestPermissions(InteractiveInputURLActivity.this, new String[]{Manifest.permission.CAMERA}, REQ_CODE_PERMISSION);
|
||||
} else {
|
||||
// Have gotten the permission
|
||||
startCaptureActivityForResult(requestCode);
|
||||
}
|
||||
}
|
||||
|
||||
private void startCaptureActivityForResult(int requestCode) {
|
||||
Intent intent = new Intent(InteractiveInputURLActivity.this, CaptureActivity.class);
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putBoolean(CaptureActivity.KEY_NEED_BEEP, CaptureActivity.VALUE_BEEP);
|
||||
bundle.putBoolean(CaptureActivity.KEY_NEED_VIBRATION, CaptureActivity.VALUE_VIBRATION);
|
||||
bundle.putBoolean(CaptureActivity.KEY_NEED_EXPOSURE, CaptureActivity.VALUE_NO_EXPOSURE);
|
||||
bundle.putByte(CaptureActivity.KEY_FLASHLIGHT_MODE, CaptureActivity.VALUE_FLASHLIGHT_OFF);
|
||||
bundle.putByte(CaptureActivity.KEY_ORIENTATION_MODE, CaptureActivity.VALUE_ORIENTATION_AUTO);
|
||||
bundle.putBoolean(CaptureActivity.KEY_SCAN_AREA_FULL_SCREEN, CaptureActivity.VALUE_SCAN_AREA_FULL_SCREEN);
|
||||
bundle.putBoolean(CaptureActivity.KEY_NEED_SCAN_HINT_TEXT, CaptureActivity.VALUE_SCAN_HINT_TEXT);
|
||||
intent.putExtra(CaptureActivity.EXTRA_SETTING_BUNDLE, bundle);
|
||||
startActivityForResult(intent, requestCode);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,460 @@
|
||||
package com.alivc.live.barestream_interactive;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.Color;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.view.View;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.EditText;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.recyclerview.widget.GridLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.acker.simplezxing.activity.CaptureActivity;
|
||||
import com.alivc.live.barestream_interactive.adapter.InteractiveMultiBareRecyclerViewAdapter;
|
||||
import com.alivc.live.barestream_interactive.bean.BareStreamBean;
|
||||
import com.alivc.live.commonui.utils.StatusBarUtil;
|
||||
import com.alivc.live.commonutils.ToastUtils;
|
||||
import com.alivc.live.interactive_common.bean.InteractiveUserData;
|
||||
import com.alivc.live.interactive_common.listener.InteractLiveTipsViewListener;
|
||||
import com.alivc.live.interactive_common.listener.InteractLivePushPullListener;
|
||||
import com.alivc.live.interactive_common.utils.InteractLiveIntent;
|
||||
import com.alivc.live.commonui.avdialog.AUILiveDialog;
|
||||
import com.alivc.live.interactive_common.widget.ConnectionLostTipsView;
|
||||
import com.alivc.live.interactive_common.widget.InteractiveCommonInputView;
|
||||
import com.alivc.live.interactive_common.widget.InteractiveRoomControlView;
|
||||
import com.alivc.live.player.annotations.AlivcLivePlayError;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class InteractiveMultiBareActivity extends AppCompatActivity {
|
||||
|
||||
private static final int REQ_CODE_PERMISSION = 0x1111;
|
||||
private static final int REQ_CODE_PUSH_URL = 0x2222;
|
||||
private static final int REQ_CODE_PULL_URL = 0x3333;
|
||||
public static final String DATA_INTERACTIVE_PUSH_URL = "data_interactive_push_url";
|
||||
public static final String DATA_INTERACTIVE_PULL_URL = "data_interactive_pull_url";
|
||||
public static final String DATA_INTERACTIVE_PULL_URL_1 = "data_interactive_pull_url_1";
|
||||
public static final String DATA_INTERACTIVE_PULL_URL_2 = "data_interactive_pull_url_2";
|
||||
public static final String DATA_INTERACTIVE_PULL_URL_3 = "data_interactive_pull_url_3";
|
||||
|
||||
private AUILiveDialog mAUILiveDialog;
|
||||
|
||||
//Dialog 弹窗的意图
|
||||
private InteractLiveIntent mCurrentIntent;
|
||||
private ImageView mCloseImageView;
|
||||
private TextView mShowConnectIdTextView;
|
||||
//大窗口
|
||||
private FrameLayout mBigFrameLayout;
|
||||
//小窗口
|
||||
private ConnectionLostTipsView mConnectionLostTipsView;
|
||||
|
||||
//输入 URL 方式连麦
|
||||
private InteractiveUserData mPushUserData;
|
||||
private InteractiveUserData mPullUserData;
|
||||
private InteractiveUserData mPullUserData1;
|
||||
private InteractiveUserData mPullUserData2;
|
||||
private InteractiveUserData mPullUserData3;
|
||||
|
||||
private InteractiveCommonInputView commonInputView;
|
||||
private InteractiveRoomControlView mInteractiveRoomControlView;
|
||||
private MultiBareStreamController mBareStreamController;
|
||||
private EditText mInteractivePushUrlEditText;
|
||||
private ImageView mPushUrlQrImageView;
|
||||
private TextView mStartPushTextView;
|
||||
private InteractiveMultiBareRecyclerViewAdapter mAdapter;
|
||||
private final List<BareStreamBean> mFakeList = new ArrayList<>();
|
||||
private RecyclerView mRecyclerView;
|
||||
private BareStreamBean mCurrentBareStreamBean;
|
||||
private int mCurrentCheckedPosition;
|
||||
private boolean mIsPushing;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
StatusBarUtil.translucent(this, Color.TRANSPARENT);
|
||||
setContentView(R.layout.activity_interactive_multi_bare);
|
||||
|
||||
InteractiveUserData pushUserData = new InteractiveUserData();
|
||||
pushUserData.url = getIntent().getStringExtra(DATA_INTERACTIVE_PUSH_URL);
|
||||
mPushUserData = pushUserData;
|
||||
|
||||
InteractiveUserData pullUserData = new InteractiveUserData();
|
||||
pullUserData.url = getIntent().getStringExtra(DATA_INTERACTIVE_PULL_URL);
|
||||
mPullUserData = pullUserData;
|
||||
|
||||
InteractiveUserData pullUserData1 = new InteractiveUserData();
|
||||
pullUserData1.url = getIntent().getStringExtra(DATA_INTERACTIVE_PULL_URL_1);
|
||||
mPullUserData1 = pullUserData1;
|
||||
|
||||
InteractiveUserData pullUserData2 = new InteractiveUserData();
|
||||
pullUserData2.url = getIntent().getStringExtra(DATA_INTERACTIVE_PULL_URL_2);
|
||||
mPullUserData2 = pullUserData2;
|
||||
|
||||
InteractiveUserData pullUserData3 = new InteractiveUserData();
|
||||
pullUserData3.url = getIntent().getStringExtra(DATA_INTERACTIVE_PULL_URL_3);
|
||||
mPullUserData3 = pullUserData3;
|
||||
|
||||
mBareStreamController = new MultiBareStreamController(this);
|
||||
|
||||
initView();
|
||||
initListener();
|
||||
initData();
|
||||
}
|
||||
|
||||
private void initData() {
|
||||
mInteractivePushUrlEditText.setText(mPushUserData != null ? mPushUserData.url : "");
|
||||
|
||||
mBareStreamController.setAnchorRenderView(mBigFrameLayout);
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
BareStreamBean bareStreamBean = new BareStreamBean();
|
||||
switch (i) {
|
||||
case 0:
|
||||
bareStreamBean.setUserData(mPullUserData);
|
||||
break;
|
||||
case 1:
|
||||
bareStreamBean.setUserData(mPullUserData1);
|
||||
break;
|
||||
case 2:
|
||||
bareStreamBean.setUserData(mPullUserData2);
|
||||
break;
|
||||
case 3:
|
||||
bareStreamBean.setUserData(mPullUserData3);
|
||||
break;
|
||||
}
|
||||
mFakeList.add(bareStreamBean);
|
||||
}
|
||||
mAdapter.setData(mFakeList);
|
||||
}
|
||||
|
||||
private void initView() {
|
||||
mAUILiveDialog = new AUILiveDialog(this);
|
||||
mCloseImageView = findViewById(R.id.iv_close);
|
||||
mShowConnectIdTextView = findViewById(R.id.tv_show_connect);
|
||||
mBigFrameLayout = findViewById(R.id.big_fl);
|
||||
mInteractiveRoomControlView = findViewById(R.id.interactive_setting_view);
|
||||
|
||||
mConnectionLostTipsView = new ConnectionLostTipsView(this);
|
||||
|
||||
mPushUrlQrImageView = findViewById(R.id.iv_qr_push_url);
|
||||
mStartPushTextView = findViewById(R.id.tv_start_push);
|
||||
mInteractivePushUrlEditText = findViewById(R.id.et_interactive_push_url);
|
||||
|
||||
mRecyclerView = findViewById(R.id.recycler_view);
|
||||
GridLayoutManager gridLayoutManager = new GridLayoutManager(this, 2);
|
||||
gridLayoutManager.setOrientation(GridLayoutManager.HORIZONTAL);
|
||||
mRecyclerView.setLayoutManager(gridLayoutManager);
|
||||
mAdapter = new InteractiveMultiBareRecyclerViewAdapter();
|
||||
mRecyclerView.setAdapter(mAdapter);
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void initListener() {
|
||||
mPushUrlQrImageView.setOnClickListener(view -> {
|
||||
startQr(REQ_CODE_PUSH_URL);
|
||||
});
|
||||
|
||||
mAdapter.setOnConnectClickListener(position -> {
|
||||
mCurrentCheckedPosition = position;
|
||||
mCurrentBareStreamBean = mFakeList.get(position);
|
||||
if (mCurrentBareStreamBean.isConnected()) {
|
||||
mCurrentIntent = InteractLiveIntent.INTENT_STOP_PULL;
|
||||
showInteractLiveDialog(getResources().getString(R.string.interact_live_connect_finish_tips), false);
|
||||
} else {
|
||||
if (TextUtils.isEmpty(mCurrentBareStreamBean.getUrl())) {
|
||||
showInteractLiveDialog(getResources().getString(R.string.interact_live_url_connect_tips), true);
|
||||
} else {
|
||||
FrameLayout renderViewByPosition = getRenderViewByPosition(position);
|
||||
if (renderViewByPosition != null) {
|
||||
mBareStreamController.startConnect(mCurrentBareStreamBean.getUserData(), renderViewByPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
mStartPushTextView.setOnClickListener(view -> {
|
||||
if (mIsPushing) {
|
||||
mBareStreamController.stopPush();
|
||||
changePushState(false);
|
||||
} else {
|
||||
InteractiveUserData userData = new InteractiveUserData();
|
||||
userData.url = mInteractivePushUrlEditText.getText().toString().trim();
|
||||
mBareStreamController.setPushData(userData);
|
||||
mBareStreamController.startPush();
|
||||
}
|
||||
});
|
||||
|
||||
mConnectionLostTipsView.setConnectionLostListener(() -> runOnUiThread(() -> finish()));
|
||||
|
||||
mBareStreamController.setMultiInteractLivePushPullListener(new InteractLivePushPullListener() {
|
||||
|
||||
@Override
|
||||
public void onPushSuccess() {
|
||||
super.onPushSuccess();
|
||||
changePushState(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPushError() {
|
||||
super.onPushError();
|
||||
changePushState(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPullSuccess(InteractiveUserData userData) {
|
||||
BareStreamBean bean = mFakeList.get(Integer.parseInt(userData.getKey()));
|
||||
if (bean != null) {
|
||||
bean.setConnected(true);
|
||||
}
|
||||
mAdapter.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPullError(InteractiveUserData userData, AlivcLivePlayError errorType, String errorMsg) {
|
||||
super.onPullError(userData, errorType, errorMsg);
|
||||
runOnUiThread(() -> {
|
||||
BareStreamBean bean = mFakeList.get(Integer.parseInt(userData.getKey()));
|
||||
if (bean != null) {
|
||||
bean.setConnected(false);
|
||||
}
|
||||
mAdapter.notifyDataSetChanged();
|
||||
mBareStreamController.stopConnect(userData);
|
||||
updateConnectTextView(false);
|
||||
ToastUtils.show(getResources().getString(R.string.interact_live_viewer_left));
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPullStop(InteractiveUserData userData) {
|
||||
runOnUiThread(() -> {
|
||||
updateConnectTextView(false);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectionLost() {
|
||||
runOnUiThread(() -> mConnectionLostTipsView.show());
|
||||
}
|
||||
});
|
||||
|
||||
mCloseImageView.setOnClickListener(view -> {
|
||||
mCurrentIntent = InteractLiveIntent.INTENT_FINISH;
|
||||
showInteractLiveDialog(getResources().getString(R.string.interact_live_leave_room_tips), false);
|
||||
});
|
||||
|
||||
mInteractiveRoomControlView.setOnClickEventListener(new InteractiveRoomControlView.OnClickEventListener() {
|
||||
@Override
|
||||
public void onClickSwitchCamera() {
|
||||
mBareStreamController.switchCamera();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClickSpeakerPhone(boolean enable) {
|
||||
mBareStreamController.enableSpeakerPhone(enable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClickMuteAudio(boolean mute) {
|
||||
mBareStreamController.setMute(mute);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClickMuteVideo(boolean mute) {
|
||||
mBareStreamController.muteLocalCamera(mute);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClickEnableAudio(boolean enable) {
|
||||
mBareStreamController.enableAudioCapture(enable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClickEnableVideo(boolean enable) {
|
||||
mBareStreamController.enableLocalCamera(enable);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void updateConnectTextView(boolean connecting) {
|
||||
mShowConnectIdTextView.setVisibility(connecting ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
|
||||
private void showInteractLiveDialog(String content, boolean showInputView) {
|
||||
commonInputView = new InteractiveCommonInputView(InteractiveMultiBareActivity.this);
|
||||
commonInputView.setViewType(InteractiveCommonInputView.ViewType.BARE_STREAM);
|
||||
commonInputView.showInputView(content, showInputView);
|
||||
mAUILiveDialog.setContentView(commonInputView);
|
||||
mAUILiveDialog.show();
|
||||
|
||||
commonInputView.setOnInteractLiveTipsViewListener(new InteractLiveTipsViewListener() {
|
||||
@Override
|
||||
public void onCancel() {
|
||||
if (mAUILiveDialog.isShowing()) {
|
||||
mAUILiveDialog.dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfirm() {
|
||||
if (mCurrentIntent == InteractLiveIntent.INTENT_STOP_PULL) {
|
||||
//主播结束连麦
|
||||
mAUILiveDialog.dismiss();
|
||||
mBareStreamController.stopConnect(mCurrentBareStreamBean.getUserData());
|
||||
updateConnectTextView(false);
|
||||
mCurrentBareStreamBean.setConnected(false);
|
||||
mAdapter.notifyDataSetChanged();
|
||||
} else if (mCurrentIntent == InteractLiveIntent.INTENT_FINISH) {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInputConfirm(InteractiveUserData userData) {
|
||||
hideInputSoftFromWindowMethod(InteractiveMultiBareActivity.this, commonInputView);
|
||||
if (userData == null || TextUtils.isEmpty(userData.url)) {
|
||||
ToastUtils.show(getResources().getString(R.string.interact_live_connect_input_error_tips));
|
||||
return;
|
||||
}
|
||||
mAUILiveDialog.dismiss();
|
||||
//主播端,输入观众 id 后,开始连麦
|
||||
mCurrentBareStreamBean.setUserData(userData);
|
||||
FrameLayout renderViewByPosition = getRenderViewByPosition(mCurrentCheckedPosition);
|
||||
mBareStreamController.startConnect(mCurrentBareStreamBean.getUserData(), renderViewByPosition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onQrClick() {
|
||||
startQr(REQ_CODE_PULL_URL);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
mBareStreamController.release();
|
||||
}
|
||||
|
||||
public void hideInputSoftFromWindowMethod(Context context, View view) {
|
||||
try {
|
||||
InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
switch (requestCode) {
|
||||
case REQ_CODE_PUSH_URL:
|
||||
case REQ_CODE_PULL_URL:
|
||||
switch (resultCode) {
|
||||
case RESULT_OK:
|
||||
if (requestCode == REQ_CODE_PUSH_URL) {
|
||||
if (mInteractivePushUrlEditText != null) {
|
||||
mInteractivePushUrlEditText.setText(data.getStringExtra(CaptureActivity.EXTRA_SCAN_RESULT));
|
||||
}
|
||||
} else {
|
||||
if (commonInputView != null) {
|
||||
commonInputView.setQrResult(data.getStringExtra(CaptureActivity.EXTRA_SCAN_RESULT));
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case RESULT_CANCELED:
|
||||
if (data != null) {
|
||||
// for some reason camera is not working correctly
|
||||
ToastUtils.show(getString(R.string.live_push_switch_to_facing_back_camera));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void changePushState(boolean isSuccess) {
|
||||
this.mIsPushing = isSuccess;
|
||||
if (isSuccess) {
|
||||
mStartPushTextView.setText(getResources().getString(com.alivc.live.interactive_common.R.string.interact_stop_push));
|
||||
mStartPushTextView.setBackground(getResources().getDrawable(com.alivc.live.interactive_common.R.drawable.shape_interact_live_un_connect_btn_bg));
|
||||
} else {
|
||||
mStartPushTextView.setText(getResources().getString(com.alivc.live.interactive_common.R.string.interact_start_push));
|
||||
mStartPushTextView.setBackground(getResources().getDrawable(com.alivc.live.interactive_common.R.drawable.shape_pysh_btn_bg));
|
||||
}
|
||||
}
|
||||
|
||||
private InteractiveMultiBareRecyclerViewAdapter.InteractiveMultiBareViewHolder getViewHolderByPosition(int position) {
|
||||
return (InteractiveMultiBareRecyclerViewAdapter.InteractiveMultiBareViewHolder) mRecyclerView.findViewHolderForAdapterPosition(position);
|
||||
}
|
||||
|
||||
private FrameLayout getRenderViewByPosition(int position) {
|
||||
InteractiveMultiBareRecyclerViewAdapter.InteractiveMultiBareViewHolder viewHolder = getViewHolderByPosition(position);
|
||||
if (viewHolder != null) {
|
||||
return viewHolder.getRenderFrameLayout();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private int getAdapterPositionFromByKey(String key) {
|
||||
for (int i = 0; i < mFakeList.size(); i++) {
|
||||
if (key.equals(mFakeList.get(i).getUrl())) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private void startQr(int requestCode) {
|
||||
if (ContextCompat.checkSelfPermission(InteractiveMultiBareActivity.this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
|
||||
// Do not have the permission of camera, request it.
|
||||
ActivityCompat.requestPermissions(InteractiveMultiBareActivity.this, new String[]{Manifest.permission.CAMERA}, REQ_CODE_PERMISSION);
|
||||
} else {
|
||||
// Have gotten the permission
|
||||
startCaptureActivityForResult(requestCode);
|
||||
}
|
||||
}
|
||||
|
||||
private void startCaptureActivityForResult(int requestCode) {
|
||||
Intent intent = new Intent(InteractiveMultiBareActivity.this, CaptureActivity.class);
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putBoolean(CaptureActivity.KEY_NEED_BEEP, CaptureActivity.VALUE_BEEP);
|
||||
bundle.putBoolean(CaptureActivity.KEY_NEED_VIBRATION, CaptureActivity.VALUE_VIBRATION);
|
||||
bundle.putBoolean(CaptureActivity.KEY_NEED_EXPOSURE, CaptureActivity.VALUE_NO_EXPOSURE);
|
||||
bundle.putByte(CaptureActivity.KEY_FLASHLIGHT_MODE, CaptureActivity.VALUE_FLASHLIGHT_OFF);
|
||||
bundle.putByte(CaptureActivity.KEY_ORIENTATION_MODE, CaptureActivity.VALUE_ORIENTATION_AUTO);
|
||||
bundle.putBoolean(CaptureActivity.KEY_SCAN_AREA_FULL_SCREEN, CaptureActivity.VALUE_SCAN_AREA_FULL_SCREEN);
|
||||
bundle.putBoolean(CaptureActivity.KEY_NEED_SCAN_HINT_TEXT, CaptureActivity.VALUE_SCAN_HINT_TEXT);
|
||||
intent.putExtra(CaptureActivity.EXTRA_SETTING_BUNDLE, bundle);
|
||||
startActivityForResult(intent, requestCode);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
package com.alivc.live.barestream_interactive;
|
||||
|
||||
import android.content.Context;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import com.alivc.live.commonbiz.LocalStreamReader;
|
||||
import com.alivc.live.commonbiz.ResourcesConst;
|
||||
import com.alivc.live.interactive_common.InteractLiveBaseManager;
|
||||
import com.alivc.live.interactive_common.InteractiveMode;
|
||||
import com.alivc.live.interactive_common.bean.InteractiveUserData;
|
||||
import com.alivc.live.interactive_common.listener.InteractLivePushPullListener;
|
||||
import com.alivc.live.interactive_common.utils.LivePushGlobalConfig;
|
||||
import com.alivc.live.pusher.AlivcResolutionEnum;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
class MultiBareStreamController {
|
||||
|
||||
private final InteractLiveBaseManager mInteractLiveManager;
|
||||
private final Context mContext;
|
||||
private final LocalStreamReader mLocalStreamReader;
|
||||
//主播预览 View
|
||||
private FrameLayout mAnchorRenderView;
|
||||
//主播推流地址
|
||||
private InteractiveUserData mPushUserData;
|
||||
|
||||
public MultiBareStreamController(Context context) {
|
||||
this.mContext = context;
|
||||
AlivcResolutionEnum resolution = LivePushGlobalConfig.mAlivcLivePushConfig.getResolution();
|
||||
int width = AlivcResolutionEnum.getResolutionWidth(resolution, LivePushGlobalConfig.mAlivcLivePushConfig.getLivePushMode());
|
||||
int height = AlivcResolutionEnum.getResolutionHeight(resolution, LivePushGlobalConfig.mAlivcLivePushConfig.getLivePushMode());
|
||||
mLocalStreamReader = new LocalStreamReader.Builder()
|
||||
.setVideoWith(width)
|
||||
.setVideoHeight(height)
|
||||
.setVideoStride(width)
|
||||
.setVideoSize(height * width * 3 / 2)
|
||||
.setVideoRotation(0)
|
||||
.setAudioSampleRate(44100)
|
||||
.setAudioChannel(1)
|
||||
.setAudioBufferSize(2048)
|
||||
.build();
|
||||
mInteractLiveManager = new InteractLiveBaseManager();
|
||||
mInteractLiveManager.init(context, InteractiveMode.MULTI_BARE_STREAM);
|
||||
}
|
||||
|
||||
public void setPushData(InteractiveUserData userData) {
|
||||
mPushUserData = userData;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置主播预览 View
|
||||
*
|
||||
* @param frameLayout 主播预览 View
|
||||
*/
|
||||
public void setAnchorRenderView(FrameLayout frameLayout) {
|
||||
this.mAnchorRenderView = frameLayout;
|
||||
}
|
||||
|
||||
public void stopPush() {
|
||||
mInteractLiveManager.stopPush();
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始直播
|
||||
*/
|
||||
public void startPush() {
|
||||
externAV();
|
||||
mInteractLiveManager.startPreviewAndPush(mPushUserData, mAnchorRenderView, true);
|
||||
}
|
||||
|
||||
private void externAV() {
|
||||
if (LivePushGlobalConfig.mAlivcLivePushConfig.isExternMainStream()) {
|
||||
File yuvFile = ResourcesConst.localCaptureYUVFilePath(mContext);
|
||||
mLocalStreamReader.readYUVData(yuvFile, (buffer, pts, videoWidth, videoHeight, videoStride, videoSize, videoRotation) -> {
|
||||
mInteractLiveManager.inputStreamVideoData(buffer, videoWidth, videoHeight, videoStride, videoSize, pts, videoRotation);
|
||||
});
|
||||
File pcmFile = ResourcesConst.localCapturePCMFilePath(mContext);
|
||||
mLocalStreamReader.readPCMData(pcmFile, (buffer, length, pts, audioSampleRate, audioChannel) -> {
|
||||
mInteractLiveManager.inputStreamAudioData(buffer, length, audioSampleRate, audioChannel, pts);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始连麦
|
||||
*/
|
||||
public void startConnect(InteractiveUserData userData, FrameLayout mRenderView) {
|
||||
mInteractLiveManager.setPullView(userData, mRenderView, false);
|
||||
mInteractLiveManager.startPullRTCStream(userData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 结束连麦
|
||||
*/
|
||||
public void stopConnect(InteractiveUserData userData) {
|
||||
mInteractLiveManager.stopPullRTCStream(userData);
|
||||
}
|
||||
|
||||
public void release() {
|
||||
mInteractLiveManager.release();
|
||||
mLocalStreamReader.stopYUV();
|
||||
mLocalStreamReader.stopPcm();
|
||||
}
|
||||
|
||||
public void setMultiInteractLivePushPullListener(InteractLivePushPullListener listener) {
|
||||
mInteractLiveManager.setInteractLivePushPullListener(listener);
|
||||
}
|
||||
|
||||
public void switchCamera() {
|
||||
mInteractLiveManager.switchCamera();
|
||||
}
|
||||
|
||||
public void enableSpeakerPhone(boolean enable) {
|
||||
mInteractLiveManager.enableSpeakerPhone(enable);
|
||||
}
|
||||
|
||||
public void setMute(boolean b) {
|
||||
mInteractLiveManager.setMute(b);
|
||||
}
|
||||
|
||||
public void enableAudioCapture(boolean enable) {
|
||||
mInteractLiveManager.enableAudioCapture(enable);
|
||||
}
|
||||
|
||||
public void muteLocalCamera(boolean muteLocalCamera) {
|
||||
mInteractLiveManager.muteLocalCamera(muteLocalCamera);
|
||||
}
|
||||
|
||||
public void enableLocalCamera(boolean enable) {
|
||||
mInteractLiveManager.enableLocalCamera(enable);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
package com.alivc.live.barestream_interactive.adapter;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.alivc.live.barestream_interactive.R;
|
||||
import com.alivc.live.barestream_interactive.bean.BareStreamBean;
|
||||
import com.alivc.live.interactive_common.widget.InteractiveConnectView;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class InteractiveMultiBareRecyclerViewAdapter extends RecyclerView.Adapter<InteractiveMultiBareRecyclerViewAdapter.InteractiveMultiBareViewHolder> {
|
||||
|
||||
private Map<Integer, Integer> mFLayoutWithPositionMap = new HashMap<>();
|
||||
private OnItemClickListener mListener;
|
||||
private List<BareStreamBean> mData;
|
||||
private OnItemViewChangedListener mOnPKItemViewChangedListener;
|
||||
|
||||
public boolean reSetFrameLayout(int position, int frameLayoutHashCode) {
|
||||
if (mFLayoutWithPositionMap.containsKey(position)) {
|
||||
if (frameLayoutHashCode == mFLayoutWithPositionMap.get(position)) {
|
||||
return false;
|
||||
} else {
|
||||
mFLayoutWithPositionMap.put(position, frameLayoutHashCode);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewAttachedToWindow(InteractiveMultiBareViewHolder holder) {
|
||||
super.onViewAttachedToWindow(holder);
|
||||
if (mOnPKItemViewChangedListener != null) {
|
||||
mOnPKItemViewChangedListener.onItemViewAttachedToWindow(holder.getAdapterPosition());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewDetachedFromWindow(InteractiveMultiBareViewHolder holder) {
|
||||
super.onViewDetachedFromWindow(holder);
|
||||
if (mOnPKItemViewChangedListener != null) {
|
||||
mOnPKItemViewChangedListener.onItemViewDetachedToWindow(holder.getAdapterPosition());
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public InteractiveMultiBareViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_multi_interactive_bare_item, parent, false);
|
||||
return new InteractiveMultiBareViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull InteractiveMultiBareViewHolder holder, int position) {
|
||||
BareStreamBean bareStreamBean = mData.get(position);
|
||||
holder.isConnected(bareStreamBean.isConnected());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return mData == null ? 0 : mData.size();
|
||||
}
|
||||
|
||||
public void setOnConnectClickListener(OnItemClickListener listener) {
|
||||
this.mListener = listener;
|
||||
}
|
||||
|
||||
public void setOnItemViewChangedListener(OnItemViewChangedListener listener) {
|
||||
this.mOnPKItemViewChangedListener = listener;
|
||||
}
|
||||
|
||||
public void setData(List<BareStreamBean> dataList) {
|
||||
this.mData = dataList;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public class InteractiveMultiBareViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
private final InteractiveConnectView mConnectView;
|
||||
private final FrameLayout mRenderFrameLayout;
|
||||
|
||||
public InteractiveMultiBareViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
mConnectView = itemView.findViewById(R.id.connect_view);
|
||||
mRenderFrameLayout = itemView.findViewById(R.id.render_frame);
|
||||
|
||||
mConnectView.setDefaultText(mConnectView.getResources().getString(R.string.interactive_bare_stream_start_pull));
|
||||
mConnectView.setConnectClickListener(() -> {
|
||||
if (mListener != null) {
|
||||
mListener.onConnectClick(getAdapterPosition());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public FrameLayout getRenderFrameLayout() {
|
||||
return mRenderFrameLayout;
|
||||
}
|
||||
|
||||
public InteractiveConnectView getConnectView() {
|
||||
return mConnectView;
|
||||
}
|
||||
|
||||
public void isConnected(boolean isConnected) {
|
||||
if (isConnected) {
|
||||
mConnectView.connected(mConnectView.getResources().getString(R.string.interactive_bare_stream_stop_pull));
|
||||
} else {
|
||||
mConnectView.unConnected(mConnectView.getResources().getString(R.string.interactive_bare_stream_start_pull));
|
||||
}
|
||||
mConnectView.getConnectFrameLayout().setVisibility(isConnected ? View.INVISIBLE : View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
public interface OnItemClickListener {
|
||||
void onConnectClick(int position);
|
||||
}
|
||||
|
||||
public interface OnItemViewChangedListener {
|
||||
void onItemViewAttachedToWindow(int position);
|
||||
|
||||
void onItemViewDetachedToWindow(int position);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.alivc.live.barestream_interactive.bean;
|
||||
|
||||
import com.alivc.live.interactive_common.bean.InteractiveUserData;
|
||||
|
||||
public class BareStreamBean {
|
||||
|
||||
private InteractiveUserData userData;
|
||||
private boolean isConnected;
|
||||
|
||||
public InteractiveUserData getUserData() {
|
||||
return userData;
|
||||
}
|
||||
|
||||
public void setUserData(InteractiveUserData userData) {
|
||||
this.userData = userData;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return userData != null ? userData.url : null;
|
||||
}
|
||||
|
||||
public boolean isConnected() {
|
||||
return isConnected;
|
||||
}
|
||||
|
||||
public void setConnected(boolean connected) {
|
||||
isConnected = connected;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/big_fl"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/wheel_black">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/small_fl"
|
||||
android:layout_width="90dp"
|
||||
android:layout_height="160dp"
|
||||
android:layout_gravity="right|bottom"
|
||||
android:layout_marginEnd="33dp"
|
||||
android:layout_marginBottom="110dp" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_close"
|
||||
android:layout_width="26dp"
|
||||
android:layout_height="26dp"
|
||||
android:layout_marginStart="20dp"
|
||||
android:src="@drawable/ic_close_white"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/tv_home_id" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_home_id"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="44dp"
|
||||
android:textColor="#FCFCFD"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_show_connect"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/interact_live_connecting"
|
||||
android:textColor="#FCFCFD"
|
||||
android:textSize="12sp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintEnd_toEndOf="@id/tv_home_id"
|
||||
app:layout_constraintStart_toStartOf="@id/tv_home_id"
|
||||
app:layout_constraintTop_toBottomOf="@id/tv_home_id" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_interactive_push_url"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="22dp"
|
||||
android:layout_marginStart="20dp"
|
||||
android:text="@string/interact_live_push_url"
|
||||
android:gravity="center"
|
||||
android:textColor="#FCFCFD"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/tv_show_connect"/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/et_interactive_push_url"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:maxLines="1"
|
||||
android:singleLine="true"
|
||||
android:textColor="#E6E7EC"
|
||||
android:textColorHint="#E6E7EC"
|
||||
android:theme="@style/AUIEditText"
|
||||
app:layout_constraintEnd_toStartOf="@id/tv_start_push"
|
||||
app:layout_constraintStart_toStartOf="@id/tv_interactive_push_url"
|
||||
app:layout_constraintTop_toBottomOf="@id/tv_interactive_push_url" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_qr_push_url"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:background="@color/transparent"
|
||||
android:layout_margin="5dp"
|
||||
android:src="@drawable/ic_scan_icon"
|
||||
app:layout_constraintBottom_toBottomOf="@id/et_interactive_push_url"
|
||||
app:layout_constraintEnd_toEndOf="@id/et_interactive_push_url"
|
||||
app:layout_constraintTop_toTopOf="@id/et_interactive_push_url" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_start_push"
|
||||
android:layout_width="94dp"
|
||||
android:layout_height="30dp"
|
||||
android:background="@drawable/shape_pysh_btn_bg"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center"
|
||||
android:includeFontPadding="false"
|
||||
android:maxLines="1"
|
||||
android:text="@string/interact_start_push"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintTop_toTopOf="@id/et_interactive_push_url"
|
||||
app:layout_constraintBottom_toBottomOf="@id/et_interactive_push_url"
|
||||
app:layout_constraintEnd_toEndOf="parent"/>
|
||||
|
||||
<com.alivc.live.interactive_common.widget.InteractiveRoomControlView
|
||||
android:id="@+id/interactive_setting_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:layout_marginTop="20dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/et_interactive_push_url"/>
|
||||
|
||||
<com.alivc.live.interactive_common.widget.InteractiveConnectView
|
||||
android:id="@+id/connect_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="30dp"
|
||||
android:layout_marginBottom="65dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -0,0 +1,142 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#1C1D22">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_back"
|
||||
android:layout_width="26dp"
|
||||
android:layout_height="26dp"
|
||||
android:layout_marginStart="20dp"
|
||||
android:src="@drawable/ic_live_action_bar_back"
|
||||
app:layout_constraintBottom_toBottomOf="@id/tv_title"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/tv_title" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="44dp"
|
||||
android:layout_marginTop="44dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/interact_live_url_title"
|
||||
android:textColor="#FCFCFD"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_setting"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="44dp"
|
||||
android:layout_marginTop="44dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/interact_setting"
|
||||
android:textColor="#FCFCFD"
|
||||
android:textSize="12sp"
|
||||
android:visibility="visible"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_confirm"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:layout_gravity="bottom|center_horizontal"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:layout_marginBottom="44dp"
|
||||
android:background="@drawable/shape_rect_blue"
|
||||
android:clickable="false"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center"
|
||||
android:includeFontPadding="false"
|
||||
android:maxLines="2"
|
||||
android:text="@string/interact_live_confirm"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="14sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent" />
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/tv_title">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.alivc.live.interactive_common.widget.InteractiveQrEditView
|
||||
android:id="@+id/push_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_marginTop="20dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:title="@string/interact_live_push_url" />
|
||||
|
||||
<com.alivc.live.interactive_common.widget.InteractiveQrEditView
|
||||
android:id="@+id/pull_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_marginTop="20dp"
|
||||
app:layout_constraintEnd_toEndOf="@id/push_view"
|
||||
app:layout_constraintStart_toStartOf="@id/push_view"
|
||||
app:layout_constraintTop_toBottomOf="@id/push_view"
|
||||
app:title="@string/interact_live_pull_url" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/more_pull_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintTop_toBottomOf="@id/pull_view">
|
||||
|
||||
<com.alivc.live.interactive_common.widget.InteractiveQrEditView
|
||||
android:id="@+id/pull_view1"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_marginTop="20dp"
|
||||
app:layout_constraintEnd_toEndOf="@id/push_view"
|
||||
app:layout_constraintStart_toStartOf="@id/push_view"
|
||||
app:layout_constraintTop_toBottomOf="@id/push_view"
|
||||
app:title="@string/interact_live_pull_url" />
|
||||
|
||||
<com.alivc.live.interactive_common.widget.InteractiveQrEditView
|
||||
android:id="@+id/pull_view2"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_marginTop="20dp"
|
||||
app:layout_constraintEnd_toEndOf="@id/push_view"
|
||||
app:layout_constraintStart_toStartOf="@id/push_view"
|
||||
app:layout_constraintTop_toBottomOf="@id/push_view"
|
||||
app:title="@string/interact_live_pull_url" />
|
||||
|
||||
<com.alivc.live.interactive_common.widget.InteractiveQrEditView
|
||||
android:id="@+id/pull_view3"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_marginTop="20dp"
|
||||
app:layout_constraintEnd_toEndOf="@id/push_view"
|
||||
app:layout_constraintStart_toStartOf="@id/push_view"
|
||||
app:layout_constraintTop_toBottomOf="@id/push_view"
|
||||
app:title="@string/interact_live_pull_url" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -0,0 +1,117 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/big_fl"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/wheel_black">
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_close"
|
||||
android:layout_width="26dp"
|
||||
android:layout_height="26dp"
|
||||
android:layout_marginStart="20dp"
|
||||
android:src="@drawable/ic_close_white"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/tv_home_id" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_home_id"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="44dp"
|
||||
android:textColor="#FCFCFD"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_show_connect"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/interact_live_connecting"
|
||||
android:textColor="#FCFCFD"
|
||||
android:textSize="12sp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintEnd_toEndOf="@id/tv_home_id"
|
||||
app:layout_constraintStart_toStartOf="@id/tv_home_id"
|
||||
app:layout_constraintTop_toBottomOf="@id/tv_home_id" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_interactive_push_url"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_marginTop="22dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/interact_live_push_url"
|
||||
android:textColor="#FCFCFD"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/tv_show_connect" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/et_interactive_push_url"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:maxLines="1"
|
||||
android:singleLine="true"
|
||||
android:textColor="#E6E7EC"
|
||||
android:textColorHint="#E6E7EC"
|
||||
android:theme="@style/AUIEditText"
|
||||
app:layout_constraintEnd_toStartOf="@id/tv_start_push"
|
||||
app:layout_constraintStart_toStartOf="@id/tv_interactive_push_url"
|
||||
app:layout_constraintTop_toBottomOf="@id/tv_interactive_push_url" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_qr_push_url"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_margin="5dp"
|
||||
android:background="@color/transparent"
|
||||
android:src="@drawable/ic_scan_icon"
|
||||
app:layout_constraintBottom_toBottomOf="@id/et_interactive_push_url"
|
||||
app:layout_constraintEnd_toEndOf="@id/et_interactive_push_url"
|
||||
app:layout_constraintTop_toTopOf="@id/et_interactive_push_url" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_start_push"
|
||||
android:layout_width="94dp"
|
||||
android:layout_height="30dp"
|
||||
android:background="@drawable/shape_pysh_btn_bg"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center"
|
||||
android:includeFontPadding="false"
|
||||
android:maxLines="1"
|
||||
android:text="@string/interact_start_push"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintBottom_toBottomOf="@id/et_interactive_push_url"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/et_interactive_push_url" />
|
||||
|
||||
<com.alivc.live.interactive_common.widget.InteractiveRoomControlView
|
||||
android:id="@+id/interactive_setting_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_marginEnd="20dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/et_interactive_push_url" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recycler_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="10dp"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/render_frame"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintDimensionRatio="w,16:9"
|
||||
android:background="@color/alivc_color_black"
|
||||
app:layout_constraintStart_toStartOf="@id/connect_view"
|
||||
app:layout_constraintEnd_toEndOf="@id/connect_view"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<com.alivc.live.interactive_common.widget.InteractiveConnectView
|
||||
android:id="@+id/connect_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -0,0 +1,4 @@
|
||||
<resources>
|
||||
<string name="interactive_bare_stream_start_pull">开始拉流</string>
|
||||
<string name="interactive_bare_stream_stop_pull">结束拉流</string>
|
||||
</resources>
|
||||
1
LiveInteractive/live_interactive/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/build
|
||||
51
LiveInteractive/live_interactive/build.gradle
Normal file
@@ -0,0 +1,51 @@
|
||||
plugins {
|
||||
id 'com.android.library'
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion androidCompileSdkVersion
|
||||
buildToolsVersion androidBuildToolsVersion
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion androidMinSdkVersion
|
||||
targetSdkVersion androidTargetSdkVersion
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
|
||||
consumerProguardFiles "consumer-rules.pro"
|
||||
|
||||
javaCompileOptions {
|
||||
annotationProcessorOptions {
|
||||
arguments = [AROUTER_MODULE_NAME: 'interactiveLive']
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation externalAndroidDesign
|
||||
|
||||
implementation externalSimpleZXing
|
||||
implementation externalARouter
|
||||
annotationProcessor externalARouterCompiler
|
||||
|
||||
// Add a downgraded version of the player sdk for the live project single build.
|
||||
if ("true".equalsIgnoreCase(allInOne)) {
|
||||
implementation externalPlayerFull
|
||||
} else {
|
||||
implementation externalPlayerFullDowngrade
|
||||
}
|
||||
|
||||
implementation project(':LiveInteractive:live_interactive_common')
|
||||
}
|
||||
0
LiveInteractive/live_interactive/consumer-rules.pro
Normal file
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.alivc.live.interactive_live">
|
||||
|
||||
<application>
|
||||
<activity
|
||||
android:name=".InteractLiveActivity"
|
||||
android:alwaysRetainTaskState="true"
|
||||
android:launchMode="singleTop"
|
||||
android:screenOrientation="portrait"
|
||||
android:theme="@style/Theme.AppCompat.Light.NoActionBar" />
|
||||
<activity
|
||||
android:name=".MultiInteractLiveActivity"
|
||||
android:alwaysRetainTaskState="true"
|
||||
android:launchMode="singleTop"
|
||||
android:screenOrientation="portrait"
|
||||
android:theme="@style/Theme.AppCompat.Light.NoActionBar" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@@ -0,0 +1,199 @@
|
||||
package com.alivc.live.interactive_live;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.TextUtils;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import com.alivc.live.commonbiz.LocalStreamReader;
|
||||
import com.alivc.live.commonbiz.ResourcesConst;
|
||||
import com.alivc.live.commonbiz.test.AliLiveStreamURLUtil;
|
||||
import com.alivc.live.interactive_common.InteractiveMode;
|
||||
import com.alivc.live.interactive_common.bean.InteractiveUserData;
|
||||
import com.alivc.live.interactive_common.listener.InteractLivePushPullListener;
|
||||
import com.alivc.live.interactive_common.utils.LivePushGlobalConfig;
|
||||
import com.alivc.live.pusher.AlivcResolutionEnum;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* 以主播身份进入连麦互动界面的 Controller
|
||||
*/
|
||||
public class AnchorController {
|
||||
|
||||
private final InteractLiveManager mInteractLiveManager;
|
||||
private final Context mContext;
|
||||
private final LocalStreamReader mLocalStreamReader;
|
||||
|
||||
//主播预览 View
|
||||
private FrameLayout mAnchorRenderView;
|
||||
//观众连麦预览 View
|
||||
private FrameLayout mViewerRenderView;
|
||||
|
||||
// 主播信息
|
||||
private InteractiveUserData mAnchorUserData;
|
||||
// 连麦观众信息
|
||||
private InteractiveUserData mAudienceUserData;
|
||||
|
||||
public AnchorController(Context context, InteractiveUserData userData) {
|
||||
mAnchorUserData = userData;
|
||||
|
||||
this.mContext = context;
|
||||
AlivcResolutionEnum resolution = LivePushGlobalConfig.mAlivcLivePushConfig.getResolution();
|
||||
int width = AlivcResolutionEnum.getResolutionWidth(resolution, LivePushGlobalConfig.mAlivcLivePushConfig.getLivePushMode());
|
||||
int height = AlivcResolutionEnum.getResolutionHeight(resolution, LivePushGlobalConfig.mAlivcLivePushConfig.getLivePushMode());
|
||||
mLocalStreamReader = new LocalStreamReader.Builder()
|
||||
.setVideoWith(width)
|
||||
.setVideoHeight(height)
|
||||
.setVideoStride(width)
|
||||
.setVideoSize(height * width * 3 / 2)
|
||||
.setVideoRotation(0)
|
||||
.setAudioSampleRate(44100)
|
||||
.setAudioChannel(1)
|
||||
.setAudioBufferSize(2048)
|
||||
.build();
|
||||
|
||||
// 1v1连麦场景下,如果开启了1080P相机采集,同时设置回调低分辨率texture
|
||||
boolean useResolution1080P = LivePushGlobalConfig.mAlivcLivePushConfig.getResolution() == AlivcResolutionEnum.RESOLUTION_1080P;
|
||||
if (useResolution1080P) {
|
||||
HashMap<String, String> extras = new HashMap<>();
|
||||
extras.put("user_specified_observer_texture_low_resolution", "TRUE");
|
||||
LivePushGlobalConfig.mAlivcLivePushConfig.setExtras(extras);
|
||||
}
|
||||
|
||||
mInteractLiveManager = new InteractLiveManager();
|
||||
mInteractLiveManager.init(context, InteractiveMode.INTERACTIVE);
|
||||
|
||||
// 1v1连麦场景下,如果开启了1080P相机采集,同时设置回调低分辨率texture
|
||||
if (useResolution1080P) {
|
||||
mInteractLiveManager.changeResolution(AlivcResolutionEnum.RESOLUTION_540P);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置主播预览 View
|
||||
*
|
||||
* @param frameLayout 主播预览 View
|
||||
*/
|
||||
public void setAnchorRenderView(FrameLayout frameLayout) {
|
||||
this.mAnchorRenderView = frameLayout;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置观众预览 View
|
||||
*
|
||||
* @param frameLayout 观众预览 View
|
||||
*/
|
||||
public void setViewerRenderView(FrameLayout frameLayout) {
|
||||
this.mViewerRenderView = frameLayout;
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始直播
|
||||
*/
|
||||
public void startPush() {
|
||||
externAV();
|
||||
mInteractLiveManager.startPreviewAndPush(mAnchorUserData, mAnchorRenderView, true);
|
||||
}
|
||||
|
||||
private void externAV() {
|
||||
if (LivePushGlobalConfig.mAlivcLivePushConfig.isExternMainStream()) {
|
||||
File yuvFile = ResourcesConst.localCaptureYUVFilePath(mContext);
|
||||
mLocalStreamReader.readYUVData(yuvFile, (buffer, pts, videoWidth, videoHeight, videoStride, videoSize, videoRotation) -> {
|
||||
mInteractLiveManager.inputStreamVideoData(buffer, videoWidth, videoHeight, videoStride, videoSize, pts, videoRotation);
|
||||
});
|
||||
File pcmFile = ResourcesConst.localCapturePCMFilePath(mContext);
|
||||
mLocalStreamReader.readPCMData(pcmFile, (buffer, length, pts, audioSampleRate, audioChannel) -> {
|
||||
mInteractLiveManager.inputStreamAudioData(buffer, length, audioSampleRate, audioChannel, pts);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始连麦
|
||||
*
|
||||
* @param userData 要连麦的观众信息
|
||||
*/
|
||||
public void startConnect(InteractiveUserData userData) {
|
||||
if (userData == null || TextUtils.isEmpty(userData.channelId) || TextUtils.isEmpty(userData.userId)) {
|
||||
return;
|
||||
}
|
||||
mAudienceUserData = userData;
|
||||
mInteractLiveManager.setPullView(userData, mViewerRenderView, false);
|
||||
mInteractLiveManager.startPullRTCStream(userData);
|
||||
mInteractLiveManager.setLiveMixTranscodingConfig(mAnchorUserData, userData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 结束连麦
|
||||
*/
|
||||
public void stopConnect() {
|
||||
mInteractLiveManager.stopPullRTCStream(mAudienceUserData);
|
||||
mInteractLiveManager.clearLiveMixTranscodingConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* 主播是否正在连麦
|
||||
*
|
||||
* @return true:正在连麦 false:没有连麦
|
||||
*/
|
||||
public boolean isOnConnected() {
|
||||
return mInteractLiveManager.isPulling(mAudienceUserData);
|
||||
}
|
||||
|
||||
public void resume() {
|
||||
mInteractLiveManager.resumePush();
|
||||
mInteractLiveManager.resumePlayRTCStream(mAudienceUserData);
|
||||
}
|
||||
|
||||
public void pause() {
|
||||
mInteractLiveManager.pausePush();
|
||||
mInteractLiveManager.pausePlayRTCStream(mAudienceUserData);
|
||||
}
|
||||
|
||||
public void release() {
|
||||
mInteractLiveManager.release();
|
||||
mLocalStreamReader.stopYUV();
|
||||
mLocalStreamReader.stopPcm();
|
||||
}
|
||||
|
||||
public void pauseVideoPlaying() {
|
||||
mInteractLiveManager.pausePlayRTCStream(mAudienceUserData);
|
||||
}
|
||||
|
||||
public void resumeVideoPlaying() {
|
||||
mInteractLiveManager.resumePlayRTCStream(mAudienceUserData);
|
||||
}
|
||||
|
||||
public void setInteractLivePushPullListener(InteractLivePushPullListener listener) {
|
||||
mInteractLiveManager.setInteractLivePushPullListener(listener);
|
||||
}
|
||||
|
||||
public void switchCamera() {
|
||||
mInteractLiveManager.switchCamera();
|
||||
}
|
||||
|
||||
public void enableSpeakerPhone(boolean enable) {
|
||||
mInteractLiveManager.enableSpeakerPhone(enable);
|
||||
}
|
||||
|
||||
public void setMute(boolean b) {
|
||||
mInteractLiveManager.setMute(b);
|
||||
}
|
||||
|
||||
public void enableAudioCapture(boolean enable) {
|
||||
mInteractLiveManager.enableAudioCapture(enable);
|
||||
}
|
||||
|
||||
public void muteLocalCamera(boolean muteLocalCamera) {
|
||||
mInteractLiveManager.muteLocalCamera(muteLocalCamera);
|
||||
}
|
||||
|
||||
public void enableLocalCamera(boolean enable) {
|
||||
mInteractLiveManager.enableLocalCamera(enable);
|
||||
}
|
||||
|
||||
public void sendSEI(String text, int payload) {
|
||||
mInteractLiveManager.sendCustomMessage(text, payload);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,630 @@
|
||||
package com.alivc.live.interactive_live;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.Color;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.view.SurfaceView;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.acker.simplezxing.activity.CaptureActivity;
|
||||
import com.alibaba.android.arouter.facade.annotation.Route;
|
||||
import com.alivc.live.commonbiz.test.AliLiveStreamURLUtil;
|
||||
import com.alivc.live.commonui.messageview.AutoScrollMessagesView;
|
||||
import com.alivc.live.commonui.widgets.LivePushTextSwitch;
|
||||
import com.alivc.live.commonui.seiview.LivePusherSEIView;
|
||||
import com.alivc.live.commonui.utils.StatusBarUtil;
|
||||
import com.alivc.live.commonutils.ToastUtils;
|
||||
import com.alivc.live.interactive_common.InteractiveConstants;
|
||||
import com.alivc.live.interactive_common.bean.InteractiveUserData;
|
||||
import com.alivc.live.interactive_common.listener.ConnectionLostListener;
|
||||
import com.alivc.live.interactive_common.listener.InteractLivePushPullListener;
|
||||
import com.alivc.live.interactive_common.listener.InteractLiveTipsViewListener;
|
||||
import com.alivc.live.interactive_common.utils.InteractLiveIntent;
|
||||
import com.alivc.live.interactive_common.utils.LivePushGlobalConfig;
|
||||
import com.alivc.live.commonui.avdialog.AUILiveDialog;
|
||||
import com.alivc.live.interactive_common.widget.ConnectionLostTipsView;
|
||||
import com.alivc.live.interactive_common.widget.InteractiveCommonInputView;
|
||||
import com.alivc.live.interactive_common.widget.InteractiveConnectView;
|
||||
import com.alivc.live.interactive_common.widget.InteractiveRoomControlView;
|
||||
import com.alivc.live.interactive_common.widget.RoomAndUserInfoView;
|
||||
import com.alivc.live.player.annotations.AlivcLivePlayError;
|
||||
import com.aliyunsdk.queen.menu.QueenBeautyMenu;
|
||||
import com.aliyunsdk.queen.menu.QueenMenuPanel;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
@Route(path = "/interactiveLive/interactLive")
|
||||
public class InteractLiveActivity extends AppCompatActivity {
|
||||
|
||||
private static final int REQ_CODE_PERMISSION = 0x1111;
|
||||
|
||||
private AUILiveDialog mAUILiveDialog;
|
||||
|
||||
private InteractiveUserData mInteractiveUserData;
|
||||
//是否是主播端
|
||||
private boolean mIsAnchor = true;
|
||||
private String mAnchorId;
|
||||
|
||||
//Dialog 弹窗的意图
|
||||
private InteractLiveIntent mCurrentIntent;
|
||||
private ImageView mCloseImageView;
|
||||
private TextView mShowConnectIdTextView;
|
||||
//大窗口
|
||||
private FrameLayout mBigFrameLayout;
|
||||
//小窗口
|
||||
private FrameLayout mSmallFrameLayout;
|
||||
private SurfaceView mBigSurfaceView;
|
||||
private AnchorController mAnchorController;
|
||||
private ViewerController mViewerController;
|
||||
private ConnectionLostTipsView mConnectionLostTipsView;
|
||||
private RoomAndUserInfoView mAnchorInfoView;
|
||||
private RoomAndUserInfoView mAudienceInfoView;
|
||||
private InteractiveCommonInputView commonInputView;
|
||||
private InteractiveConnectView mInteractiveConnectView;
|
||||
private InteractiveRoomControlView mInteractiveRoomControlView;
|
||||
private ImageView mBeautyImageView;
|
||||
|
||||
// 美颜menu
|
||||
private QueenMenuPanel mBeautyMenuPanel;
|
||||
private QueenBeautyMenu mQueenBeautyMenu;
|
||||
|
||||
private LivePusherSEIView mSeiView;
|
||||
private AutoScrollMessagesView mSeiMessageView;
|
||||
|
||||
private LivePushTextSwitch mShowCustomMessageView;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
StatusBarUtil.translucent(this, Color.TRANSPARENT);
|
||||
|
||||
setContentView(R.layout.activity_interact_live);
|
||||
|
||||
mIsAnchor = getIntent().getBooleanExtra(InteractiveConstants.DATA_TYPE_IS_ANCHOR, true);
|
||||
InteractiveUserData interactiveUserData = (InteractiveUserData) getIntent().getSerializableExtra(InteractiveConstants.DATA_TYPE_INTERACTIVE_USER_DATA);
|
||||
mInteractiveUserData = interactiveUserData;
|
||||
|
||||
if (mIsAnchor) {
|
||||
mAnchorController = new AnchorController(this, interactiveUserData);
|
||||
} else {
|
||||
mViewerController = new ViewerController(this, interactiveUserData);
|
||||
}
|
||||
|
||||
initView();
|
||||
initListener();
|
||||
initData();
|
||||
}
|
||||
|
||||
private void initData() {
|
||||
if (mIsAnchor) {
|
||||
mAnchorController.setAnchorRenderView(mBigFrameLayout);
|
||||
mAnchorController.setViewerRenderView(mSmallFrameLayout);
|
||||
mAnchorController.startPush();
|
||||
mAnchorInfoView.setUserData(mInteractiveUserData);
|
||||
} else {
|
||||
mViewerController.setAnchorRenderView(mBigFrameLayout);
|
||||
mViewerController.setViewerRenderView(mSmallFrameLayout);
|
||||
mViewerController.setAnchorCDNRenderView(mBigSurfaceView);
|
||||
changeConnectRenderView(false);
|
||||
showInteractLiveDialog(getResources().getString(R.string.interact_live_connect_author_tips), true);
|
||||
}
|
||||
}
|
||||
|
||||
private void initView() {
|
||||
mAUILiveDialog = new AUILiveDialog(this);
|
||||
mInteractiveConnectView = findViewById(R.id.connect_view);
|
||||
mBigSurfaceView = findViewById(R.id.big_surface_view);
|
||||
mCloseImageView = findViewById(R.id.iv_close);
|
||||
mShowConnectIdTextView = findViewById(R.id.tv_show_connect);
|
||||
mBigFrameLayout = findViewById(R.id.big_fl);
|
||||
mSmallFrameLayout = findViewById(R.id.small_fl);
|
||||
mInteractiveRoomControlView = findViewById(R.id.interactive_setting_view);
|
||||
mBeautyImageView = findViewById(R.id.iv_beauty);
|
||||
|
||||
mBeautyMenuPanel = QueenBeautyMenu.getPanel(this);
|
||||
mBeautyMenuPanel.onHideMenu();
|
||||
mBeautyMenuPanel.onHideValidFeatures();
|
||||
mBeautyMenuPanel.onHideCopyright();
|
||||
|
||||
mQueenBeautyMenu = findViewById(R.id.beauty_beauty_menuPanel);
|
||||
mQueenBeautyMenu.addView(mBeautyMenuPanel);
|
||||
|
||||
mSeiView = findViewById(R.id.sei_view);
|
||||
mSeiView.setSendSeiViewListener(new LivePusherSEIView.SendSeiViewListener() {
|
||||
@Override
|
||||
public void onSendSeiClick(int payload, String text) {
|
||||
if (mIsAnchor) {
|
||||
mAnchorController.sendSEI(text, payload);
|
||||
} else {
|
||||
mViewerController.sendSEI(text, payload);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
mSeiMessageView = findViewById(R.id.sei_receive_view);
|
||||
|
||||
mShowCustomMessageView = findViewById(R.id.btn_show_custom_message);
|
||||
mShowCustomMessageView.setTextViewText(getString(R.string.sei_send_custom_message_tv));
|
||||
mShowCustomMessageView.setOnSwitchToggleListener(isChecked -> {
|
||||
int visibility = isChecked ? View.VISIBLE : View.GONE;
|
||||
mSeiView.setVisibility(visibility);
|
||||
});
|
||||
|
||||
TextView mHomeIdTextView = findViewById(R.id.tv_home_id);
|
||||
|
||||
mHomeIdTextView.setText(mInteractiveUserData != null ? mInteractiveUserData.channelId : "");
|
||||
|
||||
mConnectionLostTipsView = new ConnectionLostTipsView(this);
|
||||
|
||||
mBigSurfaceView.setZOrderOnTop(true);
|
||||
mBigSurfaceView.setZOrderMediaOverlay(true);
|
||||
|
||||
mAudienceInfoView = findViewById(R.id.audience_info_view);
|
||||
mAnchorInfoView = findViewById(R.id.anchor_info_view);
|
||||
mAudienceInfoView.setVisibility(View.VISIBLE);
|
||||
mAnchorInfoView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
private void initListener() {
|
||||
//美颜
|
||||
mBeautyImageView.setOnClickListener(view -> {
|
||||
if (LivePushGlobalConfig.ENABLE_BEAUTY) {
|
||||
if (mQueenBeautyMenu.getVisibility() == View.VISIBLE) {
|
||||
mQueenBeautyMenu.setVisibility(View.GONE);
|
||||
mBeautyMenuPanel.onHideMenu();
|
||||
} else {
|
||||
mQueenBeautyMenu.setVisibility(View.VISIBLE);
|
||||
mBeautyMenuPanel.onShowMenu();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
mConnectionLostTipsView.setConnectionLostListener(new ConnectionLostListener() {
|
||||
@Override
|
||||
public void onConfirm() {
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
finish();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (mIsAnchor) {
|
||||
mAnchorController.setInteractLivePushPullListener(new InteractLivePushPullListener() {
|
||||
@Override
|
||||
public void onPullSuccess(InteractiveUserData userData) {
|
||||
super.onPullSuccess(userData);
|
||||
changeSmallSurfaceViewVisible(true);
|
||||
updateConnectTextView(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPullError(InteractiveUserData userData, AlivcLivePlayError errorType, String errorMsg) {
|
||||
super.onPullError(userData, errorType, errorMsg);
|
||||
runOnUiThread(() -> {
|
||||
changeSmallSurfaceViewVisible(false);
|
||||
mAnchorController.stopConnect();
|
||||
updateConnectTextView(false);
|
||||
ToastUtils.show(getResources().getString(R.string.interact_live_viewer_left));
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPullStop(InteractiveUserData userData) {
|
||||
super.onPullStop(userData);
|
||||
runOnUiThread(() -> {
|
||||
changeSmallSurfaceViewVisible(false);
|
||||
updateConnectTextView(false);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPushSuccess() {
|
||||
super.onPushSuccess();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPushError() {
|
||||
super.onPushError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVideoEnabled(boolean enable) {
|
||||
super.onVideoEnabled(enable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectionLost() {
|
||||
super.onConnectionLost();
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mConnectionLostTipsView.show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceiveSEIMessage(int payload, byte[] data) {
|
||||
super.onReceiveSEIMessage(payload, data);
|
||||
mSeiMessageView.appendMessage("[rtc] payload=" + payload + ", " + new String(data, StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayerSei(int payload, byte[] uuid, byte[] data) {
|
||||
super.onPlayerSei(payload, uuid, data);
|
||||
mSeiMessageView.appendMessage("[cdn] payload=" + payload + ", " + new String(data, StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceiveSEIDelay(String src, String type, String msg) {
|
||||
super.onReceiveSEIDelay(src, type, msg);
|
||||
mSeiMessageView.appendMessage("[" + src + "][" + type + "][" + msg + "ms]");
|
||||
}
|
||||
});
|
||||
} else {
|
||||
mViewerController.setInteractLivePushPullListener(new InteractLivePushPullListener() {
|
||||
@Override
|
||||
public void onPullSuccess(InteractiveUserData userData) {
|
||||
super.onPullSuccess(userData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPullError(InteractiveUserData userData, AlivcLivePlayError errorType, String errorMsg) {
|
||||
super.onPullError(userData, errorType, errorMsg);
|
||||
runOnUiThread(() -> {
|
||||
if (errorType == AlivcLivePlayError.AlivcLivePlayErrorStreamStopped) {
|
||||
finish();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPullStop(InteractiveUserData userData) {
|
||||
super.onPullStop(userData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPushSuccess() {
|
||||
super.onPushSuccess();
|
||||
if (mViewerController != null) {
|
||||
mViewerController.pullOtherStream();
|
||||
}
|
||||
runOnUiThread(() -> {
|
||||
updateConnectTextView(true);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPushError() {
|
||||
super.onPushError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectionLost() {
|
||||
super.onConnectionLost();
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mConnectionLostTipsView.show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceiveSEIMessage(int payload, byte[] data) {
|
||||
super.onReceiveSEIMessage(payload, data);
|
||||
mSeiMessageView.appendMessage("[rtc] payload=" + payload + ", " + new String(data, StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayerSei(int payload, byte[] uuid, byte[] data) {
|
||||
super.onPlayerSei(payload, uuid, data);
|
||||
|
||||
mSeiMessageView.appendMessage("[cdn] payload=" + payload + ", " + new String(data, StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceiveSEIDelay(String src, String type, String msg) {
|
||||
super.onReceiveSEIDelay(src, type, msg);
|
||||
mSeiMessageView.appendMessage("[" + src + "][" + type + "][" + msg + "ms]");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVideoEnabled(boolean enable) {
|
||||
super.onVideoEnabled(enable);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//开始连麦
|
||||
mInteractiveConnectView.setConnectClickListener(() -> {
|
||||
if (mIsAnchor) {
|
||||
if (mAnchorController.isOnConnected()) {
|
||||
//主播端停止连麦
|
||||
mCurrentIntent = InteractLiveIntent.INTENT_STOP_PULL;
|
||||
showInteractLiveDialog(getResources().getString(R.string.interact_live_connect_finish_tips), false);
|
||||
} else {
|
||||
//主播端开始连麦,输入用户 id
|
||||
showInteractLiveDialog(getResources().getString(R.string.interact_live_connect_tips), true);
|
||||
}
|
||||
} else {
|
||||
if (mViewerController.isPushing()) {
|
||||
//观众端停止连麦
|
||||
mCurrentIntent = InteractLiveIntent.INTENT_STOP_PUSH;
|
||||
showInteractLiveDialog(getResources().getString(R.string.interact_live_connect_finish_tips), false);
|
||||
} else {
|
||||
//观众端开始连麦
|
||||
if (mViewerController.hasAnchorId()) {
|
||||
changeConnectRenderView(true);
|
||||
mViewerController.startConnect();
|
||||
changeSmallSurfaceViewVisible(true);
|
||||
} else {
|
||||
showInteractLiveDialog(getResources().getString(R.string.interact_live_connect_author_tips), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
mCloseImageView.setOnClickListener(view -> {
|
||||
mCurrentIntent = InteractLiveIntent.INTENT_FINISH;
|
||||
showInteractLiveDialog(getResources().getString(R.string.interact_live_leave_room_tips), false);
|
||||
});
|
||||
|
||||
mInteractiveRoomControlView.setOnClickEventListener(new InteractiveRoomControlView.OnClickEventListener() {
|
||||
@Override
|
||||
public void onClickSwitchCamera() {
|
||||
if (mIsAnchor) {
|
||||
mAnchorController.switchCamera();
|
||||
} else {
|
||||
mViewerController.switchCamera();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClickSpeakerPhone(boolean enable) {
|
||||
if (mIsAnchor) {
|
||||
mAnchorController.enableSpeakerPhone(enable);
|
||||
} else {
|
||||
mViewerController.enableSpeakerPhone(enable);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClickMuteAudio(boolean mute) {
|
||||
if (mIsAnchor) {
|
||||
mAnchorController.setMute(mute);
|
||||
} else {
|
||||
mViewerController.setMute(mute);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClickMuteVideo(boolean mute) {
|
||||
if (mIsAnchor) {
|
||||
mAnchorController.muteLocalCamera(mute);
|
||||
} else {
|
||||
mViewerController.muteLocalCamera(mute);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClickEnableAudio(boolean enable) {
|
||||
if (mIsAnchor) {
|
||||
mAnchorController.enableAudioCapture(enable);
|
||||
} else {
|
||||
mViewerController.enableAudioCapture(enable);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClickEnableVideo(boolean enable) {
|
||||
if (mIsAnchor) {
|
||||
mAnchorController.enableLocalCamera(enable);
|
||||
} else {
|
||||
mViewerController.enableLocalCamera(enable);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void changeSmallSurfaceViewVisible(boolean isShowSurfaceView) {
|
||||
mSmallFrameLayout.setVisibility(isShowSurfaceView ? View.VISIBLE : View.INVISIBLE);
|
||||
mInteractiveConnectView.isShow(isShowSurfaceView);
|
||||
}
|
||||
|
||||
public void updateConnectTextView(boolean connecting) {
|
||||
if (connecting) {
|
||||
mShowConnectIdTextView.setVisibility(View.VISIBLE);
|
||||
mInteractiveConnectView.connected();
|
||||
} else {
|
||||
mShowConnectIdTextView.setVisibility(View.GONE);
|
||||
mInteractiveConnectView.unConnected();
|
||||
}
|
||||
}
|
||||
|
||||
private void showInteractLiveDialog(String content, boolean showInputView, boolean showQR) {
|
||||
commonInputView = new InteractiveCommonInputView(InteractLiveActivity.this);
|
||||
commonInputView.setViewType(InteractiveCommonInputView.ViewType.INTERACTIVE);
|
||||
commonInputView.showInputView(content, showInputView);
|
||||
mAUILiveDialog.setContentView(commonInputView);
|
||||
mAUILiveDialog.show();
|
||||
|
||||
commonInputView.setOnInteractLiveTipsViewListener(new InteractLiveTipsViewListener() {
|
||||
@Override
|
||||
public void onCancel() {
|
||||
if (mAUILiveDialog.isShowing()) {
|
||||
mAUILiveDialog.dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfirm() {
|
||||
if (mCurrentIntent == InteractLiveIntent.INTENT_STOP_PULL && mIsAnchor) {
|
||||
//主播结束连麦
|
||||
mAUILiveDialog.dismiss();
|
||||
mAnchorController.stopConnect();
|
||||
updateConnectTextView(false);
|
||||
changeSmallSurfaceViewVisible(false);
|
||||
} else if (mCurrentIntent == InteractLiveIntent.INTENT_STOP_PUSH && !mIsAnchor) {
|
||||
//观众结束连麦
|
||||
mAUILiveDialog.dismiss();
|
||||
mViewerController.stopConnect();
|
||||
changeConnectRenderView(false);
|
||||
updateConnectTextView(false);
|
||||
changeSmallSurfaceViewVisible(false);
|
||||
} else if (mCurrentIntent == InteractLiveIntent.INTENT_FINISH) {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInputConfirm(InteractiveUserData userData) {
|
||||
hideInputSoftFromWindowMethod(InteractLiveActivity.this, commonInputView);
|
||||
if (TextUtils.isEmpty(userData.userId)) {
|
||||
ToastUtils.show(getResources().getString(R.string.interact_live_connect_input_error_tips));
|
||||
return;
|
||||
}
|
||||
userData.channelId = mInteractiveUserData != null ? mInteractiveUserData.channelId : "";
|
||||
userData.url = AliLiveStreamURLUtil.generateInteractivePullUrl(userData.channelId, userData.userId);
|
||||
mAUILiveDialog.dismiss();
|
||||
if (mIsAnchor) {
|
||||
//主播端,输入观众 id 后,开始连麦
|
||||
mAnchorController.startConnect(userData);
|
||||
} else {
|
||||
//观众端,输入主播 id 后,观看直播
|
||||
mViewerController.updateAnchorUserData(userData);
|
||||
mViewerController.watchLive();
|
||||
setInfoView(mInteractiveUserData != null ? mInteractiveUserData.channelId : "", userData.userId,
|
||||
mInteractiveUserData != null ? mInteractiveUserData.userId : "");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onQrClick() {
|
||||
startQr();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void showInteractLiveDialog(String content, boolean showInputView) {
|
||||
showInteractLiveDialog(content, showInputView, false);
|
||||
}
|
||||
|
||||
private void changeConnectRenderView(boolean connect) {
|
||||
mBigFrameLayout.setVisibility(connect ? View.VISIBLE : View.GONE);
|
||||
mBigSurfaceView.setVisibility(connect ? View.GONE : View.VISIBLE);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
if (mIsAnchor) {
|
||||
mAnchorController.resume();
|
||||
} else {
|
||||
mViewerController.resume();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
if (mIsAnchor) {
|
||||
mAnchorController.pause();
|
||||
} else {
|
||||
mViewerController.pause();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (mIsAnchor) {
|
||||
mAnchorController.release();
|
||||
} else {
|
||||
mViewerController.release();
|
||||
}
|
||||
}
|
||||
|
||||
public void hideInputSoftFromWindowMethod(Context context, View view) {
|
||||
try {
|
||||
InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void setInfoView(String roomId, String anchorId, String audienceId) {
|
||||
mAudienceInfoView.setUserInfo(roomId, audienceId);
|
||||
mAnchorInfoView.setUserInfo(roomId, anchorId);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
switch (requestCode) {
|
||||
case CaptureActivity.REQ_CODE:
|
||||
switch (resultCode) {
|
||||
case RESULT_OK:
|
||||
if (commonInputView != null) {
|
||||
commonInputView.setQrResult(data.getStringExtra(CaptureActivity.EXTRA_SCAN_RESULT));
|
||||
}
|
||||
break;
|
||||
case RESULT_CANCELED:
|
||||
if (data != null && commonInputView != null) {
|
||||
// for some reason camera is not working correctly
|
||||
commonInputView.setQrResult(data.getStringExtra(CaptureActivity.EXTRA_SCAN_RESULT));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void startQr() {
|
||||
if (ContextCompat.checkSelfPermission(InteractLiveActivity.this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
|
||||
// Do not have the permission of camera, request it.
|
||||
ActivityCompat.requestPermissions(InteractLiveActivity.this, new String[]{Manifest.permission.CAMERA}, REQ_CODE_PERMISSION);
|
||||
} else {
|
||||
// Have gotten the permission
|
||||
startCaptureActivityForResult();
|
||||
}
|
||||
}
|
||||
|
||||
private void startCaptureActivityForResult() {
|
||||
Intent intent = new Intent(InteractLiveActivity.this, CaptureActivity.class);
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putBoolean(CaptureActivity.KEY_NEED_BEEP, CaptureActivity.VALUE_BEEP);
|
||||
bundle.putBoolean(CaptureActivity.KEY_NEED_VIBRATION, CaptureActivity.VALUE_VIBRATION);
|
||||
bundle.putBoolean(CaptureActivity.KEY_NEED_EXPOSURE, CaptureActivity.VALUE_NO_EXPOSURE);
|
||||
bundle.putByte(CaptureActivity.KEY_FLASHLIGHT_MODE, CaptureActivity.VALUE_FLASHLIGHT_OFF);
|
||||
bundle.putByte(CaptureActivity.KEY_ORIENTATION_MODE, CaptureActivity.VALUE_ORIENTATION_AUTO);
|
||||
bundle.putBoolean(CaptureActivity.KEY_SCAN_AREA_FULL_SCREEN, CaptureActivity.VALUE_SCAN_AREA_FULL_SCREEN);
|
||||
bundle.putBoolean(CaptureActivity.KEY_NEED_SCAN_HINT_TEXT, CaptureActivity.VALUE_SCAN_HINT_TEXT);
|
||||
intent.putExtra(CaptureActivity.EXTRA_SETTING_BUNDLE, bundle);
|
||||
startActivityForResult(intent, CaptureActivity.REQ_CODE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,272 @@
|
||||
package com.alivc.live.interactive_live;
|
||||
|
||||
import static com.alivc.live.interactive_common.utils.LivePushGlobalConfig.mAlivcLivePushConfig;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import com.alivc.live.commonbiz.seidelay.SEISourceType;
|
||||
import com.alivc.live.interactive_common.InteractLiveBaseManager;
|
||||
import com.alivc.live.interactive_common.InteractiveBaseUtil;
|
||||
import com.alivc.live.interactive_common.InteractiveMode;
|
||||
import com.alivc.live.interactive_common.bean.InteractiveUserData;
|
||||
import com.alivc.live.pusher.AlivcLiveMixStream;
|
||||
import com.alivc.live.pusher.AlivcLiveTranscodingConfig;
|
||||
import com.aliyun.player.AliPlayer;
|
||||
import com.aliyun.player.AliPlayerFactory;
|
||||
import com.aliyun.player.IPlayer;
|
||||
import com.aliyun.player.nativeclass.PlayerConfig;
|
||||
import com.aliyun.player.source.UrlSource;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class InteractLiveManager extends InteractLiveBaseManager {
|
||||
|
||||
// 连麦场景下,普通观众拉取主播的旁路CDN直播流
|
||||
private AliPlayer mAliPlayer;
|
||||
|
||||
@Override
|
||||
public void init(Context context, InteractiveMode interactiveMode) {
|
||||
super.init(context, interactiveMode);
|
||||
initCDNPlayer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release() {
|
||||
super.release();
|
||||
stopPullCDNStream();
|
||||
releaseCDNPlayer();
|
||||
}
|
||||
|
||||
/**
|
||||
* 连麦场景下设置混流
|
||||
*/
|
||||
public void setLiveMixTranscodingConfig(InteractiveUserData anchorUserData, InteractiveUserData audienceUserData) {
|
||||
if (anchorUserData == null || TextUtils.isEmpty(anchorUserData.channelId) || TextUtils.isEmpty(anchorUserData.userId)) {
|
||||
clearLiveMixTranscodingConfig();
|
||||
return;
|
||||
}
|
||||
|
||||
if (audienceUserData == null || TextUtils.isEmpty(audienceUserData.channelId) || TextUtils.isEmpty(audienceUserData.userId)) {
|
||||
clearLiveMixTranscodingConfig();
|
||||
return;
|
||||
}
|
||||
|
||||
if (mAlivcLivePushConfig == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mAlivcLivePusher == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ArrayList<AlivcLiveMixStream> mixStreams = new ArrayList<>();
|
||||
|
||||
// 添加主播混流窗口
|
||||
AlivcLiveMixStream anchorMixStream = new AlivcLiveMixStream();
|
||||
anchorMixStream.userId = anchorUserData.userId;
|
||||
anchorMixStream.x = 0;
|
||||
anchorMixStream.y = 0;
|
||||
anchorMixStream.width = mAlivcLivePushConfig.getWidth();
|
||||
anchorMixStream.height = mAlivcLivePushConfig.getHeight();
|
||||
anchorMixStream.zOrder = 1;
|
||||
anchorMixStream.backgroundImageUrl = "https://alivc-demo-cms.alicdn.com/versionProduct/resources/pictures/siheng.jpg";
|
||||
|
||||
mixStreams.add(anchorMixStream);
|
||||
Log.d(TAG, "anchorMixStream: " + anchorMixStream);
|
||||
|
||||
// 添加连麦观众混流窗口
|
||||
AlivcLiveMixStream audienceMixStream = new AlivcLiveMixStream();
|
||||
if (mAudienceFrameLayout != null) {
|
||||
audienceMixStream.userId = audienceUserData.userId;
|
||||
audienceMixStream.x = (int) mAudienceFrameLayout.getX() / 3;
|
||||
audienceMixStream.y = (int) mAudienceFrameLayout.getY() / 3;
|
||||
audienceMixStream.width = mAudienceFrameLayout.getWidth() / 2;
|
||||
audienceMixStream.height = mAudienceFrameLayout.getHeight() / 2;
|
||||
audienceMixStream.zOrder = 2;
|
||||
audienceMixStream.mixSourceType = InteractiveBaseUtil.covertVideoStreamType2MixSourceType(audienceUserData.videoStreamType);
|
||||
audienceMixStream.backgroundImageUrl = "https://alivc-demo-cms.alicdn.com/versionProduct/resources/pictures/lantu.jpg";
|
||||
|
||||
mixStreams.add(audienceMixStream);
|
||||
Log.d(TAG, "audienceMixStream: " + audienceMixStream);
|
||||
}
|
||||
|
||||
AlivcLiveTranscodingConfig transcodingConfig = new AlivcLiveTranscodingConfig();
|
||||
transcodingConfig.mixStreams = mixStreams;
|
||||
mAlivcLivePusher.setLiveMixTranscodingConfig(transcodingConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* 连麦场景添加混流
|
||||
*/
|
||||
public void addAnchorMixTranscodingConfig(InteractiveUserData anchorUserData) {
|
||||
if (anchorUserData == null || TextUtils.isEmpty(anchorUserData.channelId) || TextUtils.isEmpty(anchorUserData.userId)) {
|
||||
clearLiveMixTranscodingConfig();
|
||||
return;
|
||||
}
|
||||
|
||||
if (mAlivcLivePushConfig == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mAlivcLivePusher == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
AlivcLiveMixStream anchorMixStream = new AlivcLiveMixStream();
|
||||
anchorMixStream.userId = anchorUserData.userId;
|
||||
anchorMixStream.x = 0;
|
||||
anchorMixStream.y = 0;
|
||||
anchorMixStream.width = mAlivcLivePushConfig.getWidth();
|
||||
anchorMixStream.height = mAlivcLivePushConfig.getHeight();
|
||||
anchorMixStream.zOrder = 1;
|
||||
anchorMixStream.mixSourceType = InteractiveBaseUtil.covertVideoStreamType2MixSourceType(anchorUserData.videoStreamType);
|
||||
anchorMixStream.backgroundImageUrl = "https://alivc-demo-cms.alicdn.com/versionProduct/resources/pictures/siheng.jpg";
|
||||
|
||||
mMultiInteractLiveMixStreamsArray.add(anchorMixStream);
|
||||
mMixInteractLiveTranscodingConfig.mixStreams = mMultiInteractLiveMixStreamsArray;
|
||||
|
||||
mAlivcLivePusher.setLiveMixTranscodingConfig(mMixInteractLiveTranscodingConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* 多人连麦场景添加混流
|
||||
*/
|
||||
public void addAudienceMixTranscodingConfig(InteractiveUserData audienceUserData, FrameLayout frameLayout) {
|
||||
if (audienceUserData == null || TextUtils.isEmpty(audienceUserData.channelId) || TextUtils.isEmpty(audienceUserData.userId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mAlivcLivePusher == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
AlivcLiveMixStream audienceMixStream = new AlivcLiveMixStream();
|
||||
audienceMixStream.userId = audienceUserData.userId;
|
||||
audienceMixStream.x = (int) frameLayout.getX() / 3;
|
||||
audienceMixStream.y = (int) frameLayout.getY() / 3;
|
||||
audienceMixStream.width = frameLayout.getWidth() / 3;
|
||||
audienceMixStream.height = frameLayout.getHeight() / 3;
|
||||
audienceMixStream.zOrder = 2;
|
||||
audienceMixStream.mixSourceType = InteractiveBaseUtil.covertVideoStreamType2MixSourceType(audienceUserData.videoStreamType);
|
||||
audienceMixStream.backgroundImageUrl = "https://alivc-demo-cms.alicdn.com/versionProduct/resources/pictures/yiliang.png";
|
||||
|
||||
mMultiInteractLiveMixStreamsArray.add(audienceMixStream);
|
||||
|
||||
mMixInteractLiveTranscodingConfig.mixStreams = mMultiInteractLiveMixStreamsArray;
|
||||
|
||||
mAlivcLivePusher.setLiveMixTranscodingConfig(mMixInteractLiveTranscodingConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* 多人连麦场景移除混流
|
||||
*/
|
||||
public void removeAudienceLiveMixTranscodingConfig(InteractiveUserData audienceUserData, String anchorId) {
|
||||
if (audienceUserData == null || TextUtils.isEmpty(audienceUserData.channelId) || TextUtils.isEmpty(audienceUserData.userId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
AlivcLiveMixStream mixStream = findMixStreamByUserData(audienceUserData);
|
||||
if (mixStream == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
mMultiInteractLiveMixStreamsArray.remove(mixStream);
|
||||
|
||||
//Array 中只剩主播 id,说明无人连麦
|
||||
if (mMultiInteractLiveMixStreamsArray.size() == 1 && mMultiInteractLiveMixStreamsArray.get(0).userId.equals(anchorId)) {
|
||||
clearLiveMixTranscodingConfig();
|
||||
} else {
|
||||
mMixInteractLiveTranscodingConfig.mixStreams = mMultiInteractLiveMixStreamsArray;
|
||||
if (mAlivcLivePusher != null) {
|
||||
mAlivcLivePusher.setLiveMixTranscodingConfig(mMixInteractLiveTranscodingConfig);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化基础直播播放器(用于播放CDN基础直播流,如:rtmp/http-flv)
|
||||
*/
|
||||
private void initCDNPlayer() {
|
||||
mAliPlayer = AliPlayerFactory.createAliPlayer(mContext);
|
||||
|
||||
PlayerConfig playerConfig = mAliPlayer.getConfig();
|
||||
// 纯音频 或 纯视频 的flv 需要设置 以加快起播
|
||||
// TODO How to enable flv_strict_header
|
||||
// 起播缓存,越大起播越稳定,但会影响起播时间,可酌情设置
|
||||
playerConfig.mStartBufferDuration = 1000;
|
||||
// 卡顿恢复需要的缓存,网络不好的情况可以设置大一些,当前纯音频设置500还好,视频的话建议用默认值3000.
|
||||
playerConfig.mHighBufferDuration = 500;
|
||||
// 需要开启SEI监听
|
||||
playerConfig.mEnableSEI = true;
|
||||
mAliPlayer.setConfig(playerConfig);
|
||||
|
||||
mAliPlayer.setAutoPlay(true);
|
||||
|
||||
mAliPlayer.setOnErrorListener(errorInfo -> {
|
||||
mAliPlayer.prepare();
|
||||
});
|
||||
|
||||
// TODO keria: Remove the SEI function of the player first, as it will cause bytecode conflicts between the player SDK and the push SDK.
|
||||
// mAliPlayer.setOnSeiDataListener(new IPlayer.OnSeiDataListener() {
|
||||
// @Override
|
||||
// public void onSeiData(int type, byte[] uuid, byte[] data) {
|
||||
// String sei = new String(data, StandardCharsets.UTF_8);
|
||||
// mSEIDelayManager.receiveSEI(SEISourceType.CDN, sei);
|
||||
//
|
||||
// if (mInteractLivePushPullListener != null) {
|
||||
// mInteractLivePushPullListener.onPlayerSei(type, uuid, data);
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁基础直播播放器(用于播放CDN基础直播流,如:rtmp/http-flv)
|
||||
*/
|
||||
private void releaseCDNPlayer() {
|
||||
if (mAliPlayer != null) {
|
||||
mAliPlayer.release();
|
||||
mAliPlayer = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 连麦场景下,普通观众设置 CDN 拉流时,渲染的 Surface
|
||||
*
|
||||
* @param surfaceHolder 播放器渲染画面的 Surface
|
||||
*/
|
||||
public void setPullView(SurfaceHolder surfaceHolder) {
|
||||
if (mAliPlayer != null) {
|
||||
mAliPlayer.setDisplay(surfaceHolder);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 连麦场景下,普通观众开始播放主播的旁路CDN直播流
|
||||
*/
|
||||
public void startPullCDNStream(String pullUrl) {
|
||||
if (TextUtils.isEmpty(pullUrl)) {
|
||||
return;
|
||||
}
|
||||
if (mAliPlayer != null) {
|
||||
UrlSource urlSource = new UrlSource();
|
||||
urlSource.setUri(pullUrl);
|
||||
mAliPlayer.setDataSource(urlSource);
|
||||
mAliPlayer.prepare();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 连麦场景下,普通观众停止播放主播的旁路CDN直播流
|
||||
*/
|
||||
public void stopPullCDNStream() {
|
||||
if (mAliPlayer != null) {
|
||||
mAliPlayer.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
package com.alivc.live.interactive_live;
|
||||
|
||||
import android.content.Context;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import com.alivc.live.commonbiz.LocalStreamReader;
|
||||
import com.alivc.live.commonbiz.ResourcesConst;
|
||||
import com.alivc.live.interactive_common.InteractiveMode;
|
||||
import com.alivc.live.interactive_common.bean.InteractiveUserData;
|
||||
import com.alivc.live.interactive_common.listener.InteractLivePushPullListener;
|
||||
import com.alivc.live.interactive_common.utils.LivePushGlobalConfig;
|
||||
import com.alivc.live.pusher.AlivcResolutionEnum;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* 以主播身份进入多人连麦互动界面的 Controller
|
||||
*/
|
||||
public class MultiAnchorController {
|
||||
|
||||
private final InteractLiveManager mInteractLiveManager;
|
||||
private final Context mContext;
|
||||
private final LocalStreamReader mLocalStreamReader;
|
||||
//主播预览 View
|
||||
private FrameLayout mAnchorRenderView;
|
||||
|
||||
//主播推流地址
|
||||
private final InteractiveUserData mPushUserData;
|
||||
|
||||
public MultiAnchorController(Context context, InteractiveUserData userData) {
|
||||
this.mContext = context;
|
||||
AlivcResolutionEnum resolution = LivePushGlobalConfig.mAlivcLivePushConfig.getResolution();
|
||||
int width = AlivcResolutionEnum.getResolutionWidth(resolution, LivePushGlobalConfig.mAlivcLivePushConfig.getLivePushMode());
|
||||
int height = AlivcResolutionEnum.getResolutionHeight(resolution, LivePushGlobalConfig.mAlivcLivePushConfig.getLivePushMode());
|
||||
mLocalStreamReader = new LocalStreamReader.Builder()
|
||||
.setVideoWith(width)
|
||||
.setVideoHeight(height)
|
||||
.setVideoStride(width)
|
||||
.setVideoSize(height * width * 3 / 2)
|
||||
.setVideoRotation(0)
|
||||
.setAudioSampleRate(44100)
|
||||
.setAudioChannel(1)
|
||||
.setAudioBufferSize(2048)
|
||||
.build();
|
||||
mPushUserData = userData;
|
||||
mInteractLiveManager = new InteractLiveManager();
|
||||
mInteractLiveManager.init(context, InteractiveMode.MULTI_INTERACTIVE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置主播预览 View
|
||||
*
|
||||
* @param frameLayout 主播预览 View
|
||||
*/
|
||||
public void setAnchorRenderView(FrameLayout frameLayout) {
|
||||
this.mAnchorRenderView = frameLayout;
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始直播
|
||||
*/
|
||||
public void startPush() {
|
||||
externAV();
|
||||
mInteractLiveManager.startPreviewAndPush(mPushUserData, mAnchorRenderView, true);
|
||||
mInteractLiveManager.addAnchorMixTranscodingConfig(mPushUserData);
|
||||
}
|
||||
|
||||
public void startConnect(InteractiveUserData userData, FrameLayout frameLayout) {
|
||||
if (userData == null) {
|
||||
return;
|
||||
}
|
||||
mInteractLiveManager.setPullView(userData, frameLayout, false);
|
||||
mInteractLiveManager.startPullRTCStream(userData);
|
||||
mInteractLiveManager.addAudienceMixTranscodingConfig(userData, frameLayout);
|
||||
}
|
||||
|
||||
public boolean isOnConnected(String key) {
|
||||
return mInteractLiveManager.isPulling(mInteractLiveManager.getUserDataByKey(key));
|
||||
}
|
||||
|
||||
private void externAV() {
|
||||
if (LivePushGlobalConfig.mAlivcLivePushConfig.isExternMainStream()) {
|
||||
File yuvFile = ResourcesConst.localCaptureYUVFilePath(mContext);
|
||||
mLocalStreamReader.readYUVData(yuvFile, (buffer, pts, videoWidth, videoHeight, videoStride, videoSize, videoRotation) -> {
|
||||
mInteractLiveManager.inputStreamVideoData(buffer, videoWidth, videoHeight, videoStride, videoSize, pts, videoRotation);
|
||||
});
|
||||
File pcmFile = ResourcesConst.localCapturePCMFilePath(mContext);
|
||||
mLocalStreamReader.readPCMData(pcmFile, (buffer, length, pts, audioSampleRate, audioChannel) -> {
|
||||
mInteractLiveManager.inputStreamAudioData(buffer, length, audioSampleRate, audioChannel, pts);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 结束连麦
|
||||
*/
|
||||
public void stopConnect(String key) {
|
||||
InteractiveUserData userData = mInteractLiveManager.getUserDataByKey(key);
|
||||
mInteractLiveManager.stopPullRTCStream(userData);
|
||||
mInteractLiveManager.removeAudienceLiveMixTranscodingConfig(userData, mPushUserData != null ? mPushUserData.userId : "");
|
||||
}
|
||||
|
||||
public void resume() {
|
||||
mInteractLiveManager.resumePush();
|
||||
mInteractLiveManager.resumePlayRTCStream();
|
||||
}
|
||||
|
||||
public void pause() {
|
||||
mInteractLiveManager.pausePush();
|
||||
mInteractLiveManager.pausePlayRTCStream();
|
||||
}
|
||||
|
||||
public void release() {
|
||||
mInteractLiveManager.release();
|
||||
mLocalStreamReader.stopYUV();
|
||||
mLocalStreamReader.stopPcm();
|
||||
}
|
||||
|
||||
public void setMultiInteractLivePushPullListener(InteractLivePushPullListener listener) {
|
||||
mInteractLiveManager.setInteractLivePushPullListener(listener);
|
||||
}
|
||||
|
||||
public void switchCamera() {
|
||||
mInteractLiveManager.switchCamera();
|
||||
}
|
||||
|
||||
public void enableSpeakerPhone(boolean enable) {
|
||||
mInteractLiveManager.enableSpeakerPhone(enable);
|
||||
}
|
||||
|
||||
public void setMute(boolean b) {
|
||||
mInteractLiveManager.setMute(b);
|
||||
}
|
||||
|
||||
public void enableAudioCapture(boolean enable) {
|
||||
mInteractLiveManager.enableAudioCapture(enable);
|
||||
}
|
||||
|
||||
public void muteLocalCamera(boolean muteLocalCamera) {
|
||||
mInteractLiveManager.muteLocalCamera(muteLocalCamera);
|
||||
}
|
||||
|
||||
public void enableLocalCamera(boolean enable) {
|
||||
mInteractLiveManager.enableLocalCamera(enable);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,357 @@
|
||||
package com.alivc.live.interactive_live;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.view.SurfaceView;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import com.alibaba.android.arouter.facade.annotation.Route;
|
||||
import com.alivc.live.commonbiz.test.AliLiveStreamURLUtil;
|
||||
import com.alivc.live.commonui.avdialog.AUILiveDialog;
|
||||
import com.alivc.live.commonui.messageview.AutoScrollMessagesView;
|
||||
import com.alivc.live.commonui.utils.StatusBarUtil;
|
||||
import com.alivc.live.commonutils.ToastUtils;
|
||||
import com.alivc.live.interactive_common.InteractiveConstants;
|
||||
import com.alivc.live.interactive_common.bean.InteractiveUserData;
|
||||
import com.alivc.live.interactive_common.listener.ConnectionLostListener;
|
||||
import com.alivc.live.interactive_common.listener.InteractLivePushPullListener;
|
||||
import com.alivc.live.interactive_common.listener.InteractLiveTipsViewListener;
|
||||
import com.alivc.live.interactive_common.utils.InteractLiveIntent;
|
||||
import com.alivc.live.interactive_common.widget.ConnectionLostTipsView;
|
||||
import com.alivc.live.interactive_common.widget.InteractiveCommonInputView;
|
||||
import com.alivc.live.interactive_common.widget.InteractiveConnectView;
|
||||
import com.alivc.live.interactive_common.widget.InteractiveRoomControlView;
|
||||
import com.alivc.live.interactive_common.widget.MultiAlivcLiveView;
|
||||
import com.alivc.live.player.annotations.AlivcLivePlayError;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
@Route(path = "/interactiveLive/multiInteractLive")
|
||||
public class MultiInteractLiveActivity extends AppCompatActivity {
|
||||
private AUILiveDialog mAUILiveDialog;
|
||||
|
||||
//Dialog 弹窗的意图
|
||||
private InteractLiveIntent mCurrentIntent;
|
||||
private ImageView mCloseImageView;
|
||||
private TextView mShowConnectIdTextView;
|
||||
//大窗口
|
||||
private FrameLayout mBigFrameLayout;
|
||||
private SurfaceView mBigSurfaceView;
|
||||
private TextView mHomeIdTextView;
|
||||
private InteractiveUserData mAnchorUserData;
|
||||
private MultiAnchorController mMultiAnchorController;
|
||||
//根据 TextView 获取其他 View
|
||||
private final Map<TextView, MultiAlivcLiveView> mViewCombMap = new HashMap<>();
|
||||
//根据 id 获取其他 View
|
||||
private final Map<String, MultiAlivcLiveView> mIdViewCombMap = new HashMap<>();
|
||||
private ConnectionLostTipsView mConnectionLostTipsView;
|
||||
private InteractiveRoomControlView mInteractiveRoomControlView;
|
||||
private InteractiveConnectView mInteractiveConnectView1;
|
||||
private InteractiveConnectView mInteractiveConnectView2;
|
||||
private InteractiveConnectView mInteractiveConnectView3;
|
||||
private InteractiveConnectView mInteractiveConnectView4;
|
||||
private AutoScrollMessagesView mSeiMessageView;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
StatusBarUtil.translucent(this, Color.TRANSPARENT);
|
||||
|
||||
setContentView(R.layout.activity_multi_interact_live);
|
||||
|
||||
initView();
|
||||
initListener();
|
||||
initData();
|
||||
}
|
||||
|
||||
private void initData() {
|
||||
InteractiveUserData anchorUserData = (InteractiveUserData) getIntent().getSerializableExtra(InteractiveConstants.DATA_TYPE_INTERACTIVE_USER_DATA);
|
||||
mAnchorUserData = anchorUserData;
|
||||
|
||||
mMultiAnchorController = new MultiAnchorController(this, anchorUserData);
|
||||
mMultiAnchorController.setMultiInteractLivePushPullListener(new InteractLivePushPullListener() {
|
||||
@Override
|
||||
public void onPullSuccess(InteractiveUserData userData) {
|
||||
super.onPullSuccess(userData);
|
||||
if (userData == null) {
|
||||
return;
|
||||
}
|
||||
String viewKey = userData.getKey();
|
||||
MultiAlivcLiveView multiAlivcLiveView = mIdViewCombMap.get(viewKey);
|
||||
if (multiAlivcLiveView != null) {
|
||||
changeSmallSurfaceViewVisible(true, multiAlivcLiveView);
|
||||
updateConnectTextView(true, multiAlivcLiveView.getConnectTextView());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPullError(InteractiveUserData userData, AlivcLivePlayError errorType, String errorMsg) {
|
||||
super.onPullError(userData, errorType, errorMsg);
|
||||
runOnUiThread(() -> {
|
||||
if (userData == null) {
|
||||
return;
|
||||
}
|
||||
String viewKey = userData.getKey();
|
||||
mMultiAnchorController.stopConnect(viewKey);
|
||||
MultiAlivcLiveView multiAlivcLiveView = mIdViewCombMap.get(viewKey);
|
||||
if (multiAlivcLiveView != null) {
|
||||
changeSmallSurfaceViewVisible(false, multiAlivcLiveView);
|
||||
updateConnectTextView(false, multiAlivcLiveView.getConnectTextView());
|
||||
}
|
||||
ToastUtils.show(getResources().getString(R.string.interact_live_viewer_left));
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectionLost() {
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mConnectionLostTipsView.show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceiveSEIMessage(int payload, byte[] data) {
|
||||
super.onReceiveSEIMessage(payload, data);
|
||||
mSeiMessageView.appendMessage("[rtc] payload=" + payload + ", " + new String(data, StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceiveSEIDelay(String src, String type, String msg) {
|
||||
super.onReceiveSEIDelay(src, type, msg);
|
||||
mSeiMessageView.appendMessage("[" + src + "][" + type + "][" + msg + "ms]");
|
||||
}
|
||||
});
|
||||
mMultiAnchorController.setAnchorRenderView(mBigFrameLayout);
|
||||
mMultiAnchorController.startPush();
|
||||
|
||||
mHomeIdTextView.setText(anchorUserData.channelId);
|
||||
}
|
||||
|
||||
private void initView() {
|
||||
mAUILiveDialog = new AUILiveDialog(this);
|
||||
mInteractiveConnectView1 = findViewById(R.id.connect_view_1);
|
||||
mInteractiveConnectView2 = findViewById(R.id.connect_view_2);
|
||||
mInteractiveConnectView3 = findViewById(R.id.connect_view_3);
|
||||
mInteractiveConnectView4 = findViewById(R.id.connect_view_4);
|
||||
|
||||
mBigSurfaceView = findViewById(R.id.big_surface_view);
|
||||
mCloseImageView = findViewById(R.id.iv_close);
|
||||
mShowConnectIdTextView = findViewById(R.id.tv_show_connect);
|
||||
mBigFrameLayout = findViewById(R.id.big_fl);
|
||||
mInteractiveRoomControlView = findViewById(R.id.interactive_setting_view);
|
||||
mSeiMessageView = findViewById(R.id.sei_receive_view);
|
||||
|
||||
//小窗口
|
||||
FrameLayout mSmallFrameLayout1 = findViewById(R.id.small_fl_1);
|
||||
FrameLayout mSmallFrameLayout2 = findViewById(R.id.small_fl_2);
|
||||
FrameLayout mSmallFrameLayout3 = findViewById(R.id.small_fl_3);
|
||||
FrameLayout mSmallFrameLayout4 = findViewById(R.id.small_fl_4);
|
||||
|
||||
mHomeIdTextView = findViewById(R.id.tv_home_id);
|
||||
|
||||
mConnectionLostTipsView = new ConnectionLostTipsView(this);
|
||||
|
||||
mBigSurfaceView.setZOrderOnTop(true);
|
||||
mBigSurfaceView.setZOrderMediaOverlay(true);
|
||||
|
||||
MultiAlivcLiveView multiAlivcLiveView1 = new MultiAlivcLiveView(mInteractiveConnectView1.getConnectFrameLayout(), mSmallFrameLayout1, mInteractiveConnectView1.getConnectTextView());
|
||||
MultiAlivcLiveView multiAlivcLiveView2 = new MultiAlivcLiveView(mInteractiveConnectView2.getConnectFrameLayout(), mSmallFrameLayout2, mInteractiveConnectView2.getConnectTextView());
|
||||
MultiAlivcLiveView multiAlivcLiveView3 = new MultiAlivcLiveView(mInteractiveConnectView3.getConnectFrameLayout(), mSmallFrameLayout3, mInteractiveConnectView3.getConnectTextView());
|
||||
MultiAlivcLiveView multiAlivcLiveView4 = new MultiAlivcLiveView(mInteractiveConnectView4.getConnectFrameLayout(), mSmallFrameLayout4, mInteractiveConnectView4.getConnectTextView());
|
||||
|
||||
mViewCombMap.put(mInteractiveConnectView1.getConnectTextView(), multiAlivcLiveView1);
|
||||
mViewCombMap.put(mInteractiveConnectView2.getConnectTextView(), multiAlivcLiveView2);
|
||||
mViewCombMap.put(mInteractiveConnectView3.getConnectTextView(), multiAlivcLiveView3);
|
||||
mViewCombMap.put(mInteractiveConnectView4.getConnectTextView(), multiAlivcLiveView4);
|
||||
}
|
||||
|
||||
private void initListener() {
|
||||
mInteractiveRoomControlView.setOnClickEventListener(new InteractiveRoomControlView.OnClickEventListener() {
|
||||
@Override
|
||||
public void onClickSwitchCamera() {
|
||||
mMultiAnchorController.switchCamera();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClickSpeakerPhone(boolean enable) {
|
||||
mMultiAnchorController.enableSpeakerPhone(enable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClickMuteAudio(boolean mute) {
|
||||
mMultiAnchorController.setMute(mute);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClickMuteVideo(boolean mute) {
|
||||
mMultiAnchorController.muteLocalCamera(mute);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClickEnableAudio(boolean enable) {
|
||||
mMultiAnchorController.enableAudioCapture(enable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClickEnableVideo(boolean enable) {
|
||||
mMultiAnchorController.enableLocalCamera(enable);
|
||||
}
|
||||
});
|
||||
|
||||
mConnectionLostTipsView.setConnectionLostListener(new ConnectionLostListener() {
|
||||
@Override
|
||||
public void onConfirm() {
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
finish();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
//开始连麦
|
||||
mInteractiveConnectView1.setConnectClickListener(() -> {
|
||||
clickStartConnect(mInteractiveConnectView1.getConnectTextView());
|
||||
});
|
||||
mInteractiveConnectView2.setConnectClickListener(() -> {
|
||||
clickStartConnect(mInteractiveConnectView2.getConnectTextView());
|
||||
});
|
||||
mInteractiveConnectView3.setConnectClickListener(() -> {
|
||||
clickStartConnect(mInteractiveConnectView3.getConnectTextView());
|
||||
});
|
||||
mInteractiveConnectView4.setConnectClickListener(() -> {
|
||||
clickStartConnect(mInteractiveConnectView4.getConnectTextView());
|
||||
});
|
||||
|
||||
mCloseImageView.setOnClickListener(view -> {
|
||||
mCurrentIntent = InteractLiveIntent.INTENT_FINISH;
|
||||
showInteractLiveDialog(null, getResources().getString(R.string.interact_live_leave_room_tips), false);
|
||||
});
|
||||
}
|
||||
|
||||
private void clickStartConnect(TextView mCurrentTextView) {
|
||||
if (mCurrentTextView != null && mCurrentTextView.getTag() != null && mMultiAnchorController.isOnConnected(mCurrentTextView.getTag().toString())) {
|
||||
//主播端停止连麦
|
||||
mCurrentIntent = InteractLiveIntent.INTENT_STOP_PULL;
|
||||
showInteractLiveDialog(mCurrentTextView, getResources().getString(R.string.interact_live_connect_finish_tips), false);
|
||||
} else {
|
||||
//主播端开始连麦,输入用户 id
|
||||
showInteractLiveDialog(mCurrentTextView, getResources().getString(R.string.interact_live_connect_tips), true);
|
||||
}
|
||||
}
|
||||
|
||||
private void changeSmallSurfaceViewVisible(boolean isShowSurfaceView, MultiAlivcLiveView alivcLiveView) {
|
||||
alivcLiveView.getSmallFrameLayout().setVisibility(isShowSurfaceView ? View.VISIBLE : View.INVISIBLE);
|
||||
alivcLiveView.getUnConnectFrameLayout().setVisibility(isShowSurfaceView ? View.INVISIBLE : View.VISIBLE);
|
||||
}
|
||||
|
||||
public void updateConnectTextView(boolean connecting, TextView mConnectTextView) {
|
||||
if (connecting) {
|
||||
mShowConnectIdTextView.setVisibility(View.VISIBLE);
|
||||
mConnectTextView.setText(getResources().getString(R.string.interact_stop_connect));
|
||||
mConnectTextView.setBackground(getResources().getDrawable(R.drawable.shape_interact_live_un_connect_btn_bg));
|
||||
} else {
|
||||
mShowConnectIdTextView.setVisibility(View.GONE);
|
||||
mConnectTextView.setText(getResources().getString(R.string.interact_start_connect));
|
||||
mConnectTextView.setBackground(getResources().getDrawable(R.drawable.shape_pysh_btn_bg));
|
||||
mConnectTextView.setTag("");
|
||||
}
|
||||
}
|
||||
|
||||
private void showInteractLiveDialog(TextView textView, String content, boolean showInputView) {
|
||||
InteractiveCommonInputView commonInputView = new InteractiveCommonInputView(MultiInteractLiveActivity.this);
|
||||
commonInputView.setViewType(InteractiveCommonInputView.ViewType.INTERACTIVE);
|
||||
commonInputView.showInputView(content, showInputView);
|
||||
mAUILiveDialog.setContentView(commonInputView);
|
||||
mAUILiveDialog.show();
|
||||
|
||||
commonInputView.setOnInteractLiveTipsViewListener(new InteractLiveTipsViewListener() {
|
||||
@Override
|
||||
public void onCancel() {
|
||||
if (mAUILiveDialog.isShowing()) {
|
||||
mAUILiveDialog.dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfirm() {
|
||||
if (mCurrentIntent == InteractLiveIntent.INTENT_STOP_PULL) {
|
||||
//主播结束连麦
|
||||
mAUILiveDialog.dismiss();
|
||||
mMultiAnchorController.stopConnect((String) textView.getTag());
|
||||
updateConnectTextView(false, textView);
|
||||
MultiAlivcLiveView multiAlivcLiveView = mViewCombMap.get(textView);
|
||||
if (multiAlivcLiveView != null) {
|
||||
changeSmallSurfaceViewVisible(false, multiAlivcLiveView);
|
||||
}
|
||||
} else if (mCurrentIntent == InteractLiveIntent.INTENT_FINISH) {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInputConfirm(InteractiveUserData userData) {
|
||||
hideInputSoftFromWindowMethod(MultiInteractLiveActivity.this, commonInputView);
|
||||
if (userData == null || TextUtils.isEmpty(userData.userId)) {
|
||||
ToastUtils.show(getResources().getString(R.string.interact_live_connect_input_error_tips));
|
||||
return;
|
||||
}
|
||||
userData.channelId = mAnchorUserData != null ? mAnchorUserData.channelId : "";
|
||||
userData.url = AliLiveStreamURLUtil.generateInteractivePullUrl(userData.channelId, userData.userId);
|
||||
mAUILiveDialog.dismiss();
|
||||
//每个观众对应一个 AlvcLivePlayer
|
||||
String viewKey = userData.getKey();
|
||||
if (textView != null) {
|
||||
textView.setTag(viewKey);
|
||||
}
|
||||
mIdViewCombMap.put(viewKey, mViewCombMap.get(textView));
|
||||
//主播端,输入观众 id 后,开始连麦
|
||||
mMultiAnchorController.startConnect(userData, Objects.requireNonNull(mViewCombMap.get(textView)).getSmallFrameLayout());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
mMultiAnchorController.resume();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
mMultiAnchorController.pause();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
mMultiAnchorController.release();
|
||||
}
|
||||
|
||||
public void hideInputSoftFromWindowMethod(Context context, View view) {
|
||||
try {
|
||||
InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,262 @@
|
||||
package com.alivc.live.interactive_live;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.TextUtils;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.SurfaceView;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.alivc.live.commonbiz.LocalStreamReader;
|
||||
import com.alivc.live.commonbiz.ResourcesConst;
|
||||
import com.alivc.live.commonbiz.test.AliLiveStreamURLUtil;
|
||||
import com.alivc.live.interactive_common.InteractiveMode;
|
||||
import com.alivc.live.interactive_common.bean.InteractiveUserData;
|
||||
import com.alivc.live.interactive_common.listener.InteractLivePushPullListener;
|
||||
import com.alivc.live.interactive_common.utils.LivePushGlobalConfig;
|
||||
import com.alivc.live.pusher.AlivcResolutionEnum;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* 以观众身份进入连麦互动界面的 Controller
|
||||
*/
|
||||
public class ViewerController {
|
||||
|
||||
private final InteractLiveManager mInteractLiveManager;
|
||||
private final Context mContext;
|
||||
private final LocalStreamReader mLocalStreamReader;
|
||||
|
||||
//主播预览 View
|
||||
private FrameLayout mAnchorRenderView;
|
||||
//观众连麦预览 View
|
||||
private FrameLayout mViewerRenderView;
|
||||
|
||||
// 主播信息
|
||||
private InteractiveUserData mAnchorUserData;
|
||||
// 连麦观众信息
|
||||
private final InteractiveUserData mViewerUserData;
|
||||
|
||||
//主播连麦拉流地址
|
||||
private String mPullRTCUrl;
|
||||
// 主播CDN拉流地址
|
||||
private String mPullCDNUrl;
|
||||
|
||||
private boolean mNeedPullOtherStream = false;
|
||||
|
||||
public ViewerController(Context context, InteractiveUserData viewerUserData) {
|
||||
this.mContext = context;
|
||||
AlivcResolutionEnum resolution = LivePushGlobalConfig.mAlivcLivePushConfig.getResolution();
|
||||
int width = AlivcResolutionEnum.getResolutionWidth(resolution, LivePushGlobalConfig.mAlivcLivePushConfig.getLivePushMode());
|
||||
int height = AlivcResolutionEnum.getResolutionHeight(resolution, LivePushGlobalConfig.mAlivcLivePushConfig.getLivePushMode());
|
||||
mLocalStreamReader = new LocalStreamReader.Builder()
|
||||
.setVideoWith(width)
|
||||
.setVideoHeight(height)
|
||||
.setVideoStride(width)
|
||||
.setVideoSize(height * width * 3 / 2)
|
||||
.setVideoRotation(0)
|
||||
.setAudioSampleRate(44100)
|
||||
.setAudioChannel(1)
|
||||
.setAudioBufferSize(2048)
|
||||
.build();
|
||||
mViewerUserData = viewerUserData;
|
||||
|
||||
// 1v1连麦场景下,如果开启了1080P相机采集,同时设置回调低分辨率texture
|
||||
boolean useResolution1080P = LivePushGlobalConfig.mAlivcLivePushConfig.getResolution() == AlivcResolutionEnum.RESOLUTION_1080P;
|
||||
if (useResolution1080P) {
|
||||
HashMap<String, String> extras = new HashMap<>();
|
||||
extras.put("user_specified_observer_texture_low_resolution", "TRUE");
|
||||
LivePushGlobalConfig.mAlivcLivePushConfig.setExtras(extras);
|
||||
}
|
||||
|
||||
mInteractLiveManager = new InteractLiveManager();
|
||||
mInteractLiveManager.init(context, InteractiveMode.INTERACTIVE);
|
||||
|
||||
// 1v1连麦场景下,如果开启了1080P相机采集,同时设置回调低分辨率texture
|
||||
if (useResolution1080P) {
|
||||
mInteractLiveManager.changeResolution(AlivcResolutionEnum.RESOLUTION_540P);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置主播预览 View
|
||||
*
|
||||
* @param frameLayout 主播预览 View
|
||||
*/
|
||||
public void setAnchorRenderView(FrameLayout frameLayout) {
|
||||
this.mAnchorRenderView = frameLayout;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置观众预览 View
|
||||
*
|
||||
* @param frameLayout 观众预览 View
|
||||
*/
|
||||
public void setViewerRenderView(FrameLayout frameLayout) {
|
||||
this.mViewerRenderView = frameLayout;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置观看主播预览 View
|
||||
*
|
||||
* @param surfaceView 观看主播预览的 View
|
||||
*/
|
||||
public void setAnchorCDNRenderView(SurfaceView surfaceView) {
|
||||
surfaceView.getHolder().addCallback(new SurfaceHolder.Callback() {
|
||||
@Override
|
||||
public void surfaceCreated(@NonNull SurfaceHolder surfaceHolder) {
|
||||
mInteractLiveManager.setPullView(surfaceHolder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceChanged(@NonNull SurfaceHolder surfaceHolder, int i, int i1, int i2) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceDestroyed(@NonNull SurfaceHolder surfaceHolder) {
|
||||
mInteractLiveManager.setPullView(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新主播信息
|
||||
*
|
||||
* @param userData
|
||||
*/
|
||||
public void updateAnchorUserData(InteractiveUserData userData) {
|
||||
mAnchorUserData = userData;
|
||||
|
||||
// 连麦观看主播拉流地址(RTC拉流地址)
|
||||
mPullRTCUrl = AliLiveStreamURLUtil.generateInteractivePullUrl(mAnchorUserData.channelId, mAnchorUserData.userId);
|
||||
mAnchorUserData.url = mPullRTCUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* 观看直播
|
||||
*/
|
||||
public void watchLive() {
|
||||
// 旁路观看主播拉流地址(CDN拉流地址)
|
||||
mPullCDNUrl = AliLiveStreamURLUtil.generateCDNPullUrl(mAnchorUserData.channelId, mAnchorUserData.userId, LivePushGlobalConfig.mAlivcLivePushConfig.isAudioOnly());
|
||||
mInteractLiveManager.startPullCDNStream(mPullCDNUrl);
|
||||
|
||||
mNeedPullOtherStream = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 观众连麦主播
|
||||
*/
|
||||
public void startConnect() {
|
||||
externAV();
|
||||
//停止 cdn 拉流
|
||||
mInteractLiveManager.stopPullCDNStream();
|
||||
|
||||
//观众连麦推流
|
||||
mInteractLiveManager.startPreviewAndPush(mViewerUserData, mViewerRenderView, false);
|
||||
|
||||
mNeedPullOtherStream = true;
|
||||
}
|
||||
|
||||
// 先推后拉
|
||||
public void pullOtherStream() {
|
||||
if (mNeedPullOtherStream) {
|
||||
//连麦拉流
|
||||
mInteractLiveManager.setPullView(mAnchorUserData, mAnchorRenderView, true);
|
||||
mInteractLiveManager.startPullRTCStream(mAnchorUserData);
|
||||
}
|
||||
}
|
||||
|
||||
private void externAV() {
|
||||
if (LivePushGlobalConfig.mAlivcLivePushConfig.isExternMainStream()) {
|
||||
File yuvFile = ResourcesConst.localCaptureYUVFilePath(mContext);
|
||||
mLocalStreamReader.readYUVData(yuvFile, (buffer, pts, videoWidth, videoHeight, videoStride, videoSize, videoRotation) -> {
|
||||
mInteractLiveManager.inputStreamVideoData(buffer, videoWidth, videoHeight, videoStride, videoSize, pts, videoRotation);
|
||||
});
|
||||
File pcmFile = ResourcesConst.localCapturePCMFilePath(mContext);
|
||||
mLocalStreamReader.readPCMData(pcmFile, (buffer, length, pts, audioSampleRate, audioChannel) -> {
|
||||
mInteractLiveManager.inputStreamAudioData(buffer, length, audioSampleRate, audioChannel, pts);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 结束连麦
|
||||
*/
|
||||
public void stopConnect() {
|
||||
//观众停止推流
|
||||
mInteractLiveManager.stopPush();
|
||||
//大屏重新 CDN 流
|
||||
mInteractLiveManager.stopPullRTCStream(mAnchorUserData);
|
||||
mInteractLiveManager.startPullCDNStream(mPullCDNUrl);
|
||||
}
|
||||
|
||||
public boolean isPushing() {
|
||||
return mInteractLiveManager.isPushing();
|
||||
}
|
||||
|
||||
public void resume() {
|
||||
mInteractLiveManager.resumePush();
|
||||
mInteractLiveManager.resumePlayRTCStream(mAnchorUserData);
|
||||
}
|
||||
|
||||
public void pause() {
|
||||
mInteractLiveManager.pausePush();
|
||||
mInteractLiveManager.pausePlayRTCStream(mAnchorUserData);
|
||||
}
|
||||
|
||||
public void switchCamera() {
|
||||
mInteractLiveManager.switchCamera();
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否有主播 id
|
||||
*/
|
||||
public boolean hasAnchorId() {
|
||||
return mAnchorUserData != null && !TextUtils.isEmpty(mAnchorUserData.userId);
|
||||
}
|
||||
|
||||
public void setInteractLivePushPullListener(InteractLivePushPullListener listener) {
|
||||
mInteractLiveManager.setInteractLivePushPullListener(listener);
|
||||
}
|
||||
|
||||
public void release() {
|
||||
mInteractLiveManager.release();
|
||||
mInteractLiveManager.setInteractLivePushPullListener(null);
|
||||
mLocalStreamReader.stopYUV();
|
||||
mLocalStreamReader.stopPcm();
|
||||
}
|
||||
|
||||
public void pauseVideoPlaying() {
|
||||
mInteractLiveManager.pausePlayRTCStream(mAnchorUserData);
|
||||
}
|
||||
|
||||
public void resumeVideoPlaying() {
|
||||
mInteractLiveManager.resumePlayRTCStream(mAnchorUserData);
|
||||
}
|
||||
|
||||
public void setMute(boolean b) {
|
||||
mInteractLiveManager.setMute(b);
|
||||
}
|
||||
|
||||
public void enableAudioCapture(boolean enable) {
|
||||
mInteractLiveManager.enableAudioCapture(enable);
|
||||
}
|
||||
|
||||
public void enableSpeakerPhone(boolean enable) {
|
||||
mInteractLiveManager.enableSpeakerPhone(enable);
|
||||
}
|
||||
|
||||
public void muteLocalCamera(boolean muteLocalCamera) {
|
||||
mInteractLiveManager.muteLocalCamera(muteLocalCamera);
|
||||
}
|
||||
|
||||
public void enableLocalCamera(boolean enable) {
|
||||
mInteractLiveManager.enableLocalCamera(enable);
|
||||
}
|
||||
|
||||
public void sendSEI(String text, int payload) {
|
||||
mInteractLiveManager.sendCustomMessage(text, payload);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<SurfaceView
|
||||
android:id="@+id/big_surface_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/big_fl"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/wheel_black">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/small_fl"
|
||||
android:layout_width="90dp"
|
||||
android:layout_height="160dp"
|
||||
android:layout_gravity="right|bottom"
|
||||
android:layout_marginEnd="33dp"
|
||||
android:layout_marginBottom="110dp" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<com.alivc.live.commonui.messageview.AutoScrollMessagesView
|
||||
android:id="@+id/sei_receive_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="120dp"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_marginBottom="20dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_close"
|
||||
android:layout_width="26dp"
|
||||
android:layout_height="26dp"
|
||||
android:layout_marginStart="20dp"
|
||||
android:src="@drawable/ic_close_white"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/tv_home_id" />
|
||||
|
||||
<com.alivc.live.interactive_common.widget.RoomAndUserInfoView
|
||||
android:id="@+id/anchor_info_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="30dp"
|
||||
app:layout_constraintStart_toStartOf="@id/iv_close"
|
||||
app:layout_constraintTop_toBottomOf="@id/iv_close" />
|
||||
|
||||
<com.alivc.live.interactive_common.widget.InteractivePaneControlView
|
||||
android:id="@+id/anchor_ctrl_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
app:layout_constraintStart_toStartOf="@+id/anchor_info_view"
|
||||
app:layout_constraintTop_toBottomOf="@+id/anchor_info_view" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_home_id"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="44dp"
|
||||
android:textColor="#FCFCFD"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_show_connect"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/interact_live_connecting"
|
||||
android:textColor="#FCFCFD"
|
||||
android:textSize="12sp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintEnd_toEndOf="@id/tv_home_id"
|
||||
app:layout_constraintStart_toStartOf="@id/tv_home_id"
|
||||
app:layout_constraintTop_toBottomOf="@id/tv_home_id" />
|
||||
|
||||
<com.alivc.live.interactive_common.widget.InteractiveRoomControlView
|
||||
android:id="@+id/interactive_setting_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="20dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/tv_home_id"/>
|
||||
|
||||
<com.alivc.live.interactive_common.widget.InteractiveConnectView
|
||||
android:id="@+id/connect_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="30dp"
|
||||
android:layout_marginBottom="65dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_beauty"
|
||||
android:layout_width="22dp"
|
||||
android:layout_height="18dp"
|
||||
android:layout_marginTop="20dp"
|
||||
android:scaleType="centerCrop"
|
||||
android:src="@drawable/beauty_selector"
|
||||
app:layout_constraintEnd_toEndOf="@id/interactive_setting_view"
|
||||
app:layout_constraintStart_toStartOf="@id/interactive_setting_view"
|
||||
app:layout_constraintTop_toBottomOf="@id/interactive_setting_view"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="10dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/iv_beauty">
|
||||
|
||||
<com.alivc.live.commonui.widgets.LivePushTextSwitch
|
||||
android:id="@+id/btn_show_custom_message"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<com.alivc.live.commonui.seiview.LivePusherSEIView
|
||||
android:id="@+id/sei_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="10dp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toTopOf="@+id/sei_receive_view"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<com.alivc.live.interactive_common.widget.RoomAndUserInfoView
|
||||
android:id="@+id/audience_info_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
app:layout_constraintStart_toStartOf="@id/connect_view"
|
||||
app:layout_constraintTop_toTopOf="@id/connect_view" />
|
||||
|
||||
<com.alivc.live.interactive_common.widget.InteractivePaneControlView
|
||||
android:id="@+id/audience_ctrl_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
app:layout_constraintStart_toStartOf="@+id/audience_info_view"
|
||||
app:layout_constraintTop_toBottomOf="@+id/audience_info_view" />
|
||||
|
||||
<com.aliyunsdk.queen.menu.QueenBeautyMenu
|
||||
android:id="@+id/beauty_beauty_menuPanel"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:visibility="gone" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -0,0 +1,134 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<SurfaceView
|
||||
android:id="@+id/big_surface_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/big_fl"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/wheel_black">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/small_fl_1"
|
||||
android:layout_width="90dp"
|
||||
android:layout_height="160dp"
|
||||
android:layout_gravity="right|bottom"
|
||||
android:layout_marginEnd="140dp"
|
||||
android:layout_marginBottom="310dp" />
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/small_fl_2"
|
||||
android:layout_width="90dp"
|
||||
android:layout_height="160dp"
|
||||
android:layout_gravity="right|bottom"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:layout_marginBottom="310dp" />
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/small_fl_3"
|
||||
android:layout_width="90dp"
|
||||
android:layout_height="160dp"
|
||||
android:layout_gravity="right|bottom"
|
||||
android:layout_marginEnd="140dp"
|
||||
android:layout_marginBottom="75dp" />
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/small_fl_4"
|
||||
android:layout_width="90dp"
|
||||
android:layout_height="160dp"
|
||||
android:layout_gravity="right|bottom"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:layout_marginBottom="75dp" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<com.alivc.live.commonui.messageview.AutoScrollMessagesView
|
||||
android:id="@+id/sei_receive_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="120dp"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_marginTop="20dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/iv_close" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_close"
|
||||
android:layout_width="26dp"
|
||||
android:layout_height="26dp"
|
||||
android:layout_marginStart="20dp"
|
||||
android:src="@drawable/ic_close_white"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/tv_home_id" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_home_id"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="44dp"
|
||||
android:textColor="#FCFCFD"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_show_connect"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/interact_live_connecting"
|
||||
android:textColor="#FCFCFD"
|
||||
android:textSize="12sp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintEnd_toEndOf="@id/tv_home_id"
|
||||
app:layout_constraintStart_toStartOf="@id/tv_home_id"
|
||||
app:layout_constraintTop_toBottomOf="@id/tv_home_id" />
|
||||
|
||||
<com.alivc.live.interactive_common.widget.InteractiveRoomControlView
|
||||
android:id="@+id/interactive_setting_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="20dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/tv_home_id" />
|
||||
|
||||
<com.alivc.live.interactive_common.widget.InteractiveConnectView
|
||||
android:id="@+id/connect_view_1"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintStart_toStartOf="@id/connect_view_3"
|
||||
app:layout_constraintBottom_toBottomOf="@id/connect_view_2"/>
|
||||
|
||||
<com.alivc.live.interactive_common.widget.InteractiveConnectView
|
||||
android:id="@+id/connect_view_2"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="30dp"
|
||||
app:layout_constraintBottom_toTopOf="@id/connect_view_4"
|
||||
app:layout_constraintEnd_toEndOf="@id/connect_view_4" />
|
||||
|
||||
|
||||
<com.alivc.live.interactive_common.widget.InteractiveConnectView
|
||||
android:id="@+id/connect_view_3"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="20dp"
|
||||
app:layout_constraintBottom_toBottomOf="@id/connect_view_4"
|
||||
app:layout_constraintEnd_toStartOf="@id/connect_view_4" />
|
||||
|
||||
<com.alivc.live.interactive_common.widget.InteractiveConnectView
|
||||
android:id="@+id/connect_view_4"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:layout_marginBottom="30dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
1
LiveInteractive/live_interactive_common/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/build
|
||||
55
LiveInteractive/live_interactive_common/build.gradle
Normal file
@@ -0,0 +1,55 @@
|
||||
import java.text.SimpleDateFormat
|
||||
|
||||
plugins {
|
||||
id 'com.android.library'
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion androidCompileSdkVersion
|
||||
buildToolsVersion androidBuildToolsVersion
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion androidMinSdkVersion
|
||||
targetSdkVersion androidTargetSdkVersion
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
|
||||
consumerProguardFiles "consumer-rules.pro"
|
||||
|
||||
javaCompileOptions {
|
||||
annotationProcessorOptions {
|
||||
arguments = [AROUTER_MODULE_NAME :'interactiveCommon' ]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation externalAndroidDesign
|
||||
|
||||
api project(':LiveCommon:live_commonbiz')
|
||||
api project(':LiveBeauty:live_queenbeauty')
|
||||
|
||||
if (project.hasProperty("sdk_type") && "AliVCSDK_InteractiveLive".equalsIgnoreCase(sdk_type)) {
|
||||
api externalLivePusherInteractive
|
||||
} else {
|
||||
api externalLivePusher
|
||||
}
|
||||
|
||||
implementation externalSimpleZXing
|
||||
implementation externalARouter
|
||||
annotationProcessor externalARouterCompiler
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.alivc.live.interactive_common">
|
||||
|
||||
<application>
|
||||
<activity
|
||||
android:name="com.alivc.live.interactive_common.InteractiveInputActivity"
|
||||
android:alwaysRetainTaskState="true"
|
||||
android:launchMode="singleTop"
|
||||
android:screenOrientation="portrait"
|
||||
android:theme="@style/Theme.AppCompat.Light.NoActionBar" />
|
||||
<activity
|
||||
android:name="com.alivc.live.interactive_common.InteractiveAppInfoActivity"
|
||||
android:alwaysRetainTaskState="true"
|
||||
android:launchMode="singleTop"
|
||||
android:screenOrientation="portrait"
|
||||
android:theme="@style/Theme.AppCompat.Light.NoActionBar" />
|
||||
<activity
|
||||
android:name=".InteractiveSettingActivity"
|
||||
android:alwaysRetainTaskState="true"
|
||||
android:launchMode="singleTop"
|
||||
android:screenOrientation="portrait"
|
||||
android:theme="@style/Theme.AppCompat.Light.NoActionBar" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@@ -0,0 +1,103 @@
|
||||
package com.alivc.live.interactive_common;
|
||||
|
||||
import android.graphics.Color;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import com.alivc.live.commonbiz.SharedPreferenceUtils;
|
||||
|
||||
/**
|
||||
* AppInfo 相关信息录入 Fragment
|
||||
*/
|
||||
public class InteractLiveAppInfoFragment extends Fragment {
|
||||
|
||||
private TextView mAppInfoTv;
|
||||
private ImageView mArrowImageView;
|
||||
private ConstraintLayout mAppInfoConstraintLayout;
|
||||
private ImageView mEditImageView;
|
||||
private TextView mAppIdTextView;
|
||||
private TextView mAppKeyTextView;
|
||||
private TextView mPlayDomainTextView;
|
||||
private OnEditClickListener mOnEditClickListener;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.fragment_interact_live_appinfo, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
mAppInfoTv = view.findViewById(R.id.tv_app_info);
|
||||
mArrowImageView = view.findViewById(R.id.iv_arrow);
|
||||
mEditImageView = view.findViewById(R.id.iv_edit);
|
||||
mAppInfoConstraintLayout = view.findViewById(R.id.cl_app_info);
|
||||
|
||||
mAppIdTextView = view.findViewById(R.id.tv_app_id);
|
||||
mAppKeyTextView = view.findViewById(R.id.tv_app_key);
|
||||
mPlayDomainTextView = view.findViewById(R.id.tv_play_domain);
|
||||
|
||||
mArrowImageView.setColorFilter(Color.WHITE);
|
||||
|
||||
initListener();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
mAppIdTextView.setText(SharedPreferenceUtils.getAppId(getContext().getApplicationContext()));
|
||||
mAppKeyTextView.setText(SharedPreferenceUtils.getAppKey(getContext().getApplicationContext()));
|
||||
mPlayDomainTextView.setText(SharedPreferenceUtils.getPlayDomain(getContext().getApplicationContext()));
|
||||
}
|
||||
|
||||
private void initListener() {
|
||||
// 测试用,长按应用信息四个字,自动填写用户ID和房间号
|
||||
mAppInfoTv.setOnLongClickListener(new View.OnLongClickListener() {
|
||||
@Override
|
||||
public boolean onLongClick(View v) {
|
||||
if (mOnEditClickListener != null) {
|
||||
mOnEditClickListener.onLongClickAppInfoForDebug();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
mArrowImageView.setOnClickListener((view) -> {
|
||||
float mArrowImageViewRotation;
|
||||
if (mAppInfoConstraintLayout.isShown()) {
|
||||
mAppInfoConstraintLayout.setVisibility(View.GONE);
|
||||
mArrowImageViewRotation = 0;
|
||||
} else {
|
||||
mAppInfoConstraintLayout.setVisibility(View.VISIBLE);
|
||||
mArrowImageViewRotation = 180;
|
||||
}
|
||||
mArrowImageView.setRotation(mArrowImageViewRotation);
|
||||
});
|
||||
|
||||
mEditImageView.setOnClickListener((view) -> {
|
||||
if (mOnEditClickListener != null) {
|
||||
mOnEditClickListener.onEditClickListener();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public interface OnEditClickListener {
|
||||
void onLongClickAppInfoForDebug();
|
||||
|
||||
void onEditClickListener();
|
||||
}
|
||||
|
||||
public void setOnEditClickListener(OnEditClickListener listener) {
|
||||
this.mOnEditClickListener = listener;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,1211 @@
|
||||
package com.alivc.live.interactive_common;
|
||||
|
||||
import static com.alivc.live.interactive_common.utils.LivePushGlobalConfig.mAlivcLivePushConfig;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.hardware.Camera;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import com.alivc.component.custom.AlivcLivePushCustomFilter;
|
||||
import com.alivc.live.annotations.AlivcLiveAudioFrameObserverOperationMode;
|
||||
import com.alivc.live.annotations.AlivcLiveAudioFrameObserverUserDefinedInfoBitMask;
|
||||
import com.alivc.live.annotations.AlivcLiveAudioSource;
|
||||
import com.alivc.live.annotations.AlivcLiveMode;
|
||||
import com.alivc.live.annotations.AlivcLiveMuteLocalAudioMode;
|
||||
import com.alivc.live.annotations.AlivcLiveNetworkQuality;
|
||||
import com.alivc.live.annotations.AlivcLivePushKickedOutType;
|
||||
import com.alivc.live.annotations.AlivcLiveRecordMediaEvent;
|
||||
import com.alivc.live.beauty.BeautyFactory;
|
||||
import com.alivc.live.beauty.BeautyInterface;
|
||||
import com.alivc.live.beauty.constant.BeautySDKType;
|
||||
import com.alivc.live.commonbiz.backdoor.BackDoorInstance;
|
||||
import com.alivc.live.commonbiz.seidelay.SEIDelayManager;
|
||||
import com.alivc.live.commonbiz.seidelay.SEISourceType;
|
||||
import com.alivc.live.commonbiz.seidelay.api.ISEIDelayEventListener;
|
||||
import com.alivc.live.commonbiz.test.AliLiveStreamURLUtil;
|
||||
import com.alivc.live.commonutils.ToastUtils;
|
||||
import com.alivc.live.interactive_common.bean.InteractiveLivePlayer;
|
||||
import com.alivc.live.interactive_common.bean.InteractiveUserData;
|
||||
import com.alivc.live.interactive_common.listener.InteractLivePushPullListener;
|
||||
import com.alivc.live.interactive_common.manager.TimestampWatermarkManager;
|
||||
import com.alivc.live.interactive_common.utils.LivePushGlobalConfig;
|
||||
import com.alivc.live.player.AlivcLivePlayConfig;
|
||||
import com.alivc.live.player.AlivcLivePlayer;
|
||||
import com.alivc.live.player.annotations.AlivcLivePlayError;
|
||||
import com.alivc.live.player.annotations.AlivcLivePlayVideoStreamType;
|
||||
import com.alivc.live.pusher.AlivcAudioChannelEnum;
|
||||
import com.alivc.live.pusher.AlivcAudioSampleRateEnum;
|
||||
import com.alivc.live.pusher.AlivcImageFormat;
|
||||
import com.alivc.live.pusher.AlivcLiveAudioEffectConfig;
|
||||
import com.alivc.live.pusher.AlivcLiveAudioFrameObserverConfig;
|
||||
import com.alivc.live.pusher.AlivcLiveBase;
|
||||
import com.alivc.live.pusher.AlivcLiveLocalRecordConfig;
|
||||
import com.alivc.live.pusher.AlivcLiveMixStream;
|
||||
import com.alivc.live.pusher.AlivcLivePublishState;
|
||||
import com.alivc.live.pusher.AlivcLivePushAudioEffectVoiceChangeMode;
|
||||
import com.alivc.live.pusher.AlivcLivePushAudioFrame;
|
||||
import com.alivc.live.pusher.AlivcLivePushAudioFrameListener;
|
||||
import com.alivc.live.pusher.AlivcLivePushError;
|
||||
import com.alivc.live.pusher.AlivcLivePushErrorListener;
|
||||
import com.alivc.live.pusher.AlivcLivePushExternalAudioStreamConfig;
|
||||
import com.alivc.live.pusher.AlivcLivePushInfoListener;
|
||||
import com.alivc.live.pusher.AlivcLivePushNetworkListener;
|
||||
import com.alivc.live.pusher.AlivcLivePushStatsInfo;
|
||||
import com.alivc.live.pusher.AlivcLivePushVideoConfig;
|
||||
import com.alivc.live.pusher.AlivcLivePusher;
|
||||
import com.alivc.live.pusher.AlivcLivePusherRawDataSample;
|
||||
import com.alivc.live.pusher.AlivcLiveTranscodingConfig;
|
||||
import com.alivc.live.pusher.AlivcPreviewDisplayMode;
|
||||
import com.alivc.live.pusher.AlivcResolutionEnum;
|
||||
import com.alivc.live.pusher.AlivcSoundFormat;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 直播连麦基础类
|
||||
*
|
||||
* @note 请参考 API 文档,了解对应接口的释义,辅助接入
|
||||
* @note 互动模式下,请使用`AlivcRTC`作为日志tag,进行自主排障。
|
||||
*/
|
||||
public class InteractLiveBaseManager {
|
||||
|
||||
static {
|
||||
// 1. 注册推流SDK
|
||||
AlivcLiveBase.registerSDK();
|
||||
}
|
||||
|
||||
protected static final String TAG = "InteractLiveBaseManager";
|
||||
|
||||
protected InteractiveMode mInteractiveMode;
|
||||
|
||||
// 这个TM用来做混流的
|
||||
protected FrameLayout mAudienceFrameLayout;
|
||||
|
||||
//多人连麦混流
|
||||
protected final ArrayList<AlivcLiveMixStream> mMultiInteractLiveMixStreamsArray = new ArrayList<>();
|
||||
|
||||
//多人连麦 Config
|
||||
protected final AlivcLiveTranscodingConfig mMixInteractLiveTranscodingConfig = new AlivcLiveTranscodingConfig();
|
||||
|
||||
protected Context mContext;
|
||||
|
||||
protected AlivcLivePusher mAlivcLivePusher;
|
||||
private InteractiveUserData mPushUserData;
|
||||
|
||||
protected final Map<String, InteractiveLivePlayer> mInteractiveLivePlayerMap = new HashMap<>();
|
||||
protected final Map<String, InteractiveUserData> mInteractiveUserDataMap = new HashMap<>();
|
||||
|
||||
protected InteractLivePushPullListener mInteractLivePushPullListener;
|
||||
|
||||
// 美颜处理类,需对接Queen美颜SDK,且拥有License拥有美颜能力
|
||||
private BeautyInterface mBeautyManager;
|
||||
// CameraId,美颜需要使用
|
||||
private int mCameraId;
|
||||
|
||||
// 互动模式下的水印处理类
|
||||
private TimestampWatermarkManager mTimestampWatermarkManager;
|
||||
|
||||
protected final SEIDelayManager mSEIDelayManager = new SEIDelayManager();
|
||||
|
||||
public void init(Context context, InteractiveMode interactiveMode) {
|
||||
mInteractiveMode = interactiveMode;
|
||||
mContext = context.getApplicationContext();
|
||||
initLivePusher(interactiveMode);
|
||||
// initWatermarks();
|
||||
mSEIDelayManager.registerReceiver(new ISEIDelayEventListener() {
|
||||
@Override
|
||||
public void onEvent(String src, String type, String msg) {
|
||||
if (mInteractLivePushPullListener != null) {
|
||||
mInteractLivePushPullListener.onReceiveSEIDelay(src, type, msg);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 2. 初始化 AlivcLivePushConfig 和 AlivcLivePusher
|
||||
*
|
||||
* @param interactiveMode 直播连麦模式(直播连麦、RTS 2.0推拉裸流)
|
||||
*/
|
||||
private void initLivePusher(InteractiveMode interactiveMode) {
|
||||
// 初始化推流配置类
|
||||
|
||||
// 设置推流模式为 AlivcLiveInteractiveMode,即:互动模式;互动直播,支持连麦、PK等实时互动
|
||||
mAlivcLivePushConfig.setLivePushMode(AlivcLiveMode.AlivcLiveInteractiveMode);
|
||||
// 互动模式下,如需与Web连麦互通,必须使用H5兼容模式,否则,Web用户查看Native用户将是黑屏。
|
||||
mAlivcLivePushConfig.setH5CompatibleMode(LivePushGlobalConfig.IS_H5_COMPATIBLE);
|
||||
|
||||
// 互动模式下开启RTS推拉裸流(直推&直拉,不同于直播连麦)
|
||||
if (InteractiveMode.isBareStream(interactiveMode)) {
|
||||
mAlivcLivePushConfig.setEnableRTSForInteractiveMode(true);
|
||||
}
|
||||
|
||||
/// 如果是外部音视频推流,需要初始化的 Pusher 配置,以及自定义音频、视频的Profiles
|
||||
if (mAlivcLivePushConfig.isExternMainStream()) {
|
||||
mAlivcLivePushConfig.setExternMainStream(true, AlivcImageFormat.IMAGE_FORMAT_YUVNV12, AlivcSoundFormat.SOUND_FORMAT_S16);
|
||||
mAlivcLivePushConfig.setAudioChannels(AlivcAudioChannelEnum.AUDIO_CHANNEL_ONE);
|
||||
mAlivcLivePushConfig.setAudioSampleRate(AlivcAudioSampleRateEnum.AUDIO_SAMPLE_RATE_44100);
|
||||
}
|
||||
|
||||
// 如果需要开启 Data Channel 自定义消息通道,以代替 SEI 功能,需要预先开启 Data Channel 自定义消息通道
|
||||
if (LivePushGlobalConfig.IS_DATA_CHANNEL_MESSAGE_ENABLE) {
|
||||
mAlivcLivePushConfig.setEnableDataChannelMessage(true);
|
||||
}
|
||||
|
||||
// 开启强制耳返
|
||||
HashMap<String, String> earbackExtras = new HashMap<>(4);
|
||||
earbackExtras.put("earback_open_when_without_headset", LivePushGlobalConfig.IS_EARBACK_OPEN_WITHOUT_HEADSET ? "TRUE" : "FALSE");
|
||||
mAlivcLivePushConfig.setExtras(earbackExtras);
|
||||
|
||||
// Demo逻辑,用于测试,请勿follow!如果强制RTC预发环境,那么增加extra配置
|
||||
if (BackDoorInstance.getInstance().isForceRTCPreEnvironment()) {
|
||||
HashMap<String, String> extras = new HashMap<>(4);
|
||||
extras.put("user_specified_environment", "PRE_RELEASE");
|
||||
mAlivcLivePushConfig.setExtras(extras);
|
||||
}
|
||||
|
||||
// 初始化推流引擎
|
||||
mAlivcLivePusher = new AlivcLivePusher();
|
||||
mAlivcLivePusher.init(mContext.getApplicationContext(), mAlivcLivePushConfig);
|
||||
|
||||
// 设置音量回调频率和平滑系数
|
||||
mAlivcLivePusher.enableAudioVolumeIndication(300, 3, 1);
|
||||
|
||||
/*
|
||||
* 错误异常及特殊场景处理
|
||||
* 参考文档:https://help.aliyun.com/zh/live/developer-reference/handling-of-exceptions-and-special-scenarios-for-android
|
||||
*/
|
||||
|
||||
// 设置推流错误事件
|
||||
mAlivcLivePusher.setLivePushErrorListener(new AlivcLivePushErrorListener() {
|
||||
@Override
|
||||
public void onSystemError(AlivcLivePusher alivcLivePusher, AlivcLivePushError alivcLivePushError) {
|
||||
Log.e(TAG, "onSystemError: " + alivcLivePushError);
|
||||
if (mInteractLivePushPullListener != null) {
|
||||
mInteractLivePushPullListener.onPushError();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSDKError(AlivcLivePusher alivcLivePusher, AlivcLivePushError alivcLivePushError) {
|
||||
Log.e(TAG, "onSDKError: " + alivcLivePushError);
|
||||
if (mInteractLivePushPullListener != null) {
|
||||
mInteractLivePushPullListener.onPushError();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 设置推流通知事件
|
||||
mAlivcLivePusher.setLivePushInfoListener(new AlivcLivePushInfoListener() {
|
||||
@Override
|
||||
public void onPreviewStarted(AlivcLivePusher alivcLivePusher) {
|
||||
Log.d(TAG, "onPreviewStarted: ");
|
||||
if (mInteractLivePushPullListener != null) {
|
||||
mInteractLivePushPullListener.onPushSuccess();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPreviewStopped(AlivcLivePusher pusher) {
|
||||
Log.d(TAG, "onPreviewStopped: ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPushStarted(AlivcLivePusher alivcLivePusher) {
|
||||
Log.d(TAG, "onPushStarted: ");
|
||||
if (mInteractLivePushPullListener != null) {
|
||||
mInteractLivePushPullListener.onPushSuccess();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPushPaused(AlivcLivePusher pusher) {
|
||||
Log.d(TAG, "onPushPaused: ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPushResumed(AlivcLivePusher alivcLivePusher) {
|
||||
Log.d(TAG, "onPushResumed: ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPushStopped(AlivcLivePusher pusher) {
|
||||
Log.d(TAG, "onPushStopped: ");
|
||||
pusher.stopPreview();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPushRestarted(AlivcLivePusher alivcLivePusher) {
|
||||
Log.d(TAG, "onPushRestarted: ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFirstFramePushed(AlivcLivePusher pusher) {
|
||||
Log.d(TAG, "onFirstFramePushed: ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFirstFramePreviewed(AlivcLivePusher alivcLivePusher) {
|
||||
Log.d(TAG, "onFirstFramePreviewed: ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDropFrame(AlivcLivePusher alivcLivePusher, int i, int i1) {
|
||||
Log.d(TAG, "onDropFrame: ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAdjustBitrate(AlivcLivePusher pusher, int currentBitrate, int targetBitrate) {
|
||||
Log.i(TAG, "onAdjustBitrate: " + currentBitrate + "->" + targetBitrate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAdjustFps(AlivcLivePusher alivcLivePusher, int i, int i1) {
|
||||
Log.d(TAG, "onAdjustFps: ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPushStatistics(AlivcLivePusher alivcLivePusher, AlivcLivePushStatsInfo alivcLivePushStatsInfo) {
|
||||
// Log.i(TAG, "onPushStatistics: " + alivcLivePushStatsInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetLiveMixTranscodingConfig(AlivcLivePusher alivcLivePusher, boolean b, String s) {
|
||||
Log.d(TAG, "onSetLiveMixTranscodingConfig: ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onKickedOutByServer(AlivcLivePusher pusher, AlivcLivePushKickedOutType kickedOutType) {
|
||||
Log.d(TAG, "onKickedOutByServer: " + kickedOutType);
|
||||
if (mInteractLivePushPullListener != null) {
|
||||
mInteractLivePushPullListener.onPushError();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMicrophoneVolumeUpdate(AlivcLivePusher pusher, int volume) {
|
||||
// 麦克风音量回调(仅互动模式下生效,需设置AlivcLivePusher#enableAudioVolumeIndication接口)
|
||||
// Log.d(TAG, "onMicrophoneVolumeUpdate: " + volume);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLocalRecordEvent(AlivcLiveRecordMediaEvent mediaEvent, String storagePath) {
|
||||
ToastUtils.show(mediaEvent + ", " + storagePath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRemoteUserEnterRoom(AlivcLivePusher pusher, String userId, boolean isOnline) {
|
||||
ToastUtils.show("onRemoteUserEnterRoom: " + userId + ", isOnline" + isOnline);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRemoteUserAudioStream(AlivcLivePusher pusher, String userId, boolean isPushing) {
|
||||
ToastUtils.show("onRemoteUserAudioStream: " + userId + ", isPushing: " + isPushing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRemoteUserVideoStream(AlivcLivePusher pusher, String userId, AlivcLivePlayVideoStreamType videoStreamType, boolean isPushing) {
|
||||
ToastUtils.show("onRemoteUserVideoStream: " + userId + ", videoStreamType: " + videoStreamType + ", isPushing: " + isPushing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAudioPublishStateChanged(AlivcLivePublishState oldState, AlivcLivePublishState newState) {
|
||||
super.onAudioPublishStateChanged(oldState, newState);
|
||||
ToastUtils.show("onAudioPublishStateChanged: " + oldState + "->" + newState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVideoPublishStateChanged(AlivcLivePublishState oldState, AlivcLivePublishState newState) {
|
||||
super.onVideoPublishStateChanged(oldState, newState);
|
||||
ToastUtils.show("onVideoPublishStateChanged: " + oldState + "->" + newState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onScreenSharePublishStateChanged(AlivcLivePublishState oldState, AlivcLivePublishState newState) {
|
||||
super.onScreenSharePublishStateChanged(oldState, newState);
|
||||
ToastUtils.show("onScreenSharePublishStateChanged: " + oldState + "->" + newState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLocalDualAudioStreamPushState(AlivcLivePusher pusher, boolean isPushing) {
|
||||
super.onLocalDualAudioStreamPushState(pusher, isPushing);
|
||||
ToastUtils.show("onLocalDualAudioStreamPushState: " + isPushing);
|
||||
}
|
||||
});
|
||||
|
||||
// 设置网络通知事件
|
||||
mAlivcLivePusher.setLivePushNetworkListener(new AlivcLivePushNetworkListener() {
|
||||
@Override
|
||||
public void onNetworkPoor(AlivcLivePusher alivcLivePusher) {
|
||||
Log.d(TAG, "onNetworkPoor: ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNetworkRecovery(AlivcLivePusher alivcLivePusher) {
|
||||
Log.d(TAG, "onNetworkRecovery: ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReconnectStart(AlivcLivePusher alivcLivePusher) {
|
||||
Log.d(TAG, "onReconnectStart: ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectionLost(AlivcLivePusher alivcLivePusher) {
|
||||
Log.d(TAG, "onConnectionLost: ");
|
||||
if (mInteractLivePushPullListener != null) {
|
||||
mInteractLivePushPullListener.onConnectionLost();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReconnectFail(AlivcLivePusher alivcLivePusher) {
|
||||
Log.d(TAG, "onReconnectFail: ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReconnectSucceed(AlivcLivePusher alivcLivePusher) {
|
||||
Log.d(TAG, "onReconnectSucceed: ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSendDataTimeout(AlivcLivePusher alivcLivePusher) {
|
||||
Log.d(TAG, "onSendDataTimeout: ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectFail(AlivcLivePusher alivcLivePusher) {
|
||||
Log.d(TAG, "onConnectFail: ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNetworkQualityChanged(AlivcLiveNetworkQuality upQuality, AlivcLiveNetworkQuality downQuality) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String onPushURLAuthenticationOverdue(AlivcLivePusher alivcLivePusher) {
|
||||
Log.d(TAG, "onPushURLAuthenticationOverdue: ");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPushURLTokenWillExpire(AlivcLivePusher pusher) {
|
||||
Log.d(TAG, "onPushURLTokenWillExpire: ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPushURLTokenExpired(AlivcLivePusher pusher) {
|
||||
Log.d(TAG, "onPushURLTokenExpired: ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSendMessage(AlivcLivePusher alivcLivePusher) {
|
||||
Log.d(TAG, "onSendMessage: ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketsLost(AlivcLivePusher alivcLivePusher) {
|
||||
Log.d(TAG, "onPacketsLost: ");
|
||||
}
|
||||
});
|
||||
|
||||
// 【美颜】注册视频前处理回调
|
||||
mAlivcLivePusher.setCustomFilter(new AlivcLivePushCustomFilter() {
|
||||
@Override
|
||||
public void customFilterCreate() {
|
||||
initBeautyManager();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int customFilterProcess(int inputTexture, int textureWidth, int textureHeight, long extra) {
|
||||
if (mBeautyManager == null) {
|
||||
return inputTexture;
|
||||
}
|
||||
|
||||
return mBeautyManager.onTextureInput(inputTexture, textureWidth, textureHeight);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void customFilterDestroy() {
|
||||
destroyBeautyManager();
|
||||
}
|
||||
});
|
||||
|
||||
// 记录当前摄像头状态
|
||||
mCameraId = mAlivcLivePushConfig.getCameraType();
|
||||
}
|
||||
|
||||
private void destroyLivePusher() {
|
||||
if (mAlivcLivePusher != null) {
|
||||
mAlivcLivePusher.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isPushing() {
|
||||
return mAlivcLivePusher != null && mAlivcLivePusher.isPushing();
|
||||
}
|
||||
|
||||
public boolean isPulling(InteractiveUserData userData) {
|
||||
if (userData == null) {
|
||||
return false;
|
||||
}
|
||||
InteractiveLivePlayer interactiveLivePlayer = mInteractiveLivePlayerMap.get(userData.getKey());
|
||||
if (interactiveLivePlayer != null) {
|
||||
return interactiveLivePlayer.isPulling();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void initWatermarks() {
|
||||
// 互动模式下使用该接口添加水印,最多添加一个,多则替代
|
||||
mTimestampWatermarkManager = new TimestampWatermarkManager();
|
||||
mTimestampWatermarkManager.init(new TimestampWatermarkManager.OnWatermarkListener() {
|
||||
@Override
|
||||
public void onWatermarkUpdate(Bitmap bitmap) {
|
||||
if (mAlivcLivePusher != null) {
|
||||
mAlivcLivePusher.addWaterMark(bitmap, 0, 0, 1);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void destroyWatermarks() {
|
||||
if (mTimestampWatermarkManager != null) {
|
||||
mTimestampWatermarkManager.destroy();
|
||||
mTimestampWatermarkManager = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void setInteractLivePushPullListener(InteractLivePushPullListener listener) {
|
||||
mInteractLivePushPullListener = listener;
|
||||
}
|
||||
|
||||
private boolean createAlivcLivePlayer(InteractiveUserData userData) {
|
||||
if (userData == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String userDataKey = userData.getKey();
|
||||
if (mInteractiveLivePlayerMap.containsKey(userDataKey)) {
|
||||
Log.i(TAG, "createAlivcLivePlayer already created " + userDataKey);
|
||||
return false;
|
||||
}
|
||||
|
||||
Log.d(TAG, "createAlivcLivePlayer: " + userDataKey);
|
||||
InteractiveLivePlayer alivcLivePlayer = new InteractiveLivePlayer(mContext, AlivcLiveMode.AlivcLiveInteractiveMode);
|
||||
alivcLivePlayer.setPullUserData(userData);
|
||||
alivcLivePlayer.setMultiInteractPlayInfoListener(new InteractLivePushPullListener() {
|
||||
@Override
|
||||
public void onPullSuccess(InteractiveUserData userData) {
|
||||
if (mInteractLivePushPullListener != null) {
|
||||
mInteractLivePushPullListener.onPullSuccess(userData);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPullError(InteractiveUserData userData, AlivcLivePlayError errorType, String errorMsg) {
|
||||
if (mInteractLivePushPullListener != null) {
|
||||
mInteractLivePushPullListener.onPullError(userData, errorType, errorMsg);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPullStop(InteractiveUserData userData) {
|
||||
if (mInteractLivePushPullListener != null) {
|
||||
mInteractLivePushPullListener.onPullStop(userData);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPushSuccess() {
|
||||
if (mInteractLivePushPullListener != null) {
|
||||
mInteractLivePushPullListener.onPushSuccess();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPushError() {
|
||||
if (mInteractLivePushPullListener != null) {
|
||||
mInteractLivePushPullListener.onPushError();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceiveSEIMessage(int payload, byte[] data) {
|
||||
super.onReceiveSEIMessage(payload, data);
|
||||
if (mInteractLivePushPullListener != null) {
|
||||
mInteractLivePushPullListener.onReceiveSEIMessage(payload, data);
|
||||
}
|
||||
String sei = new String(data, StandardCharsets.UTF_8);
|
||||
mSEIDelayManager.receiveSEI(SEISourceType.RTC, sei);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVideoEnabled(boolean enable) {
|
||||
super.onVideoEnabled(enable);
|
||||
if (mInteractLivePushPullListener != null) {
|
||||
mInteractLivePushPullListener.onVideoEnabled(enable);
|
||||
}
|
||||
}
|
||||
});
|
||||
mInteractiveLivePlayerMap.put(userDataKey, alivcLivePlayer);
|
||||
mInteractiveUserDataMap.put(userDataKey, userData);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void startPreviewAndPush(InteractiveUserData userData, FrameLayout frameLayout, boolean isAnchor) {
|
||||
if (userData == null) {
|
||||
Log.e(TAG, "startPreviewAndPush, userData invalid!");
|
||||
return;
|
||||
}
|
||||
|
||||
mPushUserData = userData;
|
||||
mAlivcLivePusher.startPreview(mContext, frameLayout, isAnchor);
|
||||
|
||||
String pushUrl = userData.url;
|
||||
if (TextUtils.isEmpty(pushUrl) && InteractiveMode.isInteractive(mInteractiveMode)) {
|
||||
pushUrl = AliLiveStreamURLUtil.generateInteractivePushUrl(userData.channelId, userData.userId);
|
||||
}
|
||||
|
||||
if (TextUtils.isEmpty(pushUrl)) {
|
||||
Log.e(TAG, "startPreviewAndPush, pushUrl invalid!");
|
||||
return;
|
||||
}
|
||||
|
||||
Log.d(TAG, "startPreviewAndPush: " + isAnchor + ", " + pushUrl);
|
||||
mAlivcLivePusher.startPushAsync(pushUrl);
|
||||
|
||||
mSEIDelayManager.registerProvider(userData.userId, new ISEIDelayEventListener() {
|
||||
@Override
|
||||
public void onEvent(String src, String type, String msg) {
|
||||
sendCustomMessage(msg, 5);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void stopPreview() {
|
||||
mAlivcLivePusher.stopPreview();
|
||||
}
|
||||
|
||||
public void updatePreview(FrameLayout frameLayout, boolean isFullScreen) {
|
||||
if (mAlivcLivePusher != null) {
|
||||
mAlivcLivePusher.updatePreview(mContext, frameLayout, isFullScreen);
|
||||
}
|
||||
}
|
||||
|
||||
public void stopPush() {
|
||||
if (isPushing()) {
|
||||
mAlivcLivePusher.stopPush();
|
||||
}
|
||||
}
|
||||
|
||||
public void stopCamera() {
|
||||
mAlivcLivePusher.stopCamera();
|
||||
}
|
||||
|
||||
public void setPullView(InteractiveUserData userData, FrameLayout frameLayout, boolean isAnchor) {
|
||||
if (userData == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.mAudienceFrameLayout = frameLayout;
|
||||
|
||||
boolean isNew = createAlivcLivePlayer(userData);
|
||||
|
||||
AlivcLivePlayer livePlayer = mInteractiveLivePlayerMap.get(userData.getKey());
|
||||
if (livePlayer == null) {
|
||||
Log.e(TAG, "setPullView error: livePlayer is empty");
|
||||
return;
|
||||
}
|
||||
|
||||
if (isNew) {
|
||||
AlivcLivePlayConfig config = new AlivcLivePlayConfig();
|
||||
config.isFullScreen = isAnchor;
|
||||
config.videoStreamType = userData.videoStreamType;
|
||||
config.audioStreamType = userData.audioStreamType;
|
||||
livePlayer.setupWithConfig(config);
|
||||
}
|
||||
|
||||
livePlayer.setPlayView(frameLayout);
|
||||
Log.i(TAG, "setPullView: " + userData);
|
||||
}
|
||||
|
||||
public void startPullRTCStream(InteractiveUserData userData) {
|
||||
if (userData == null || TextUtils.isEmpty(userData.url)) {
|
||||
Log.e(TAG, "startPull error: url is empty");
|
||||
return;
|
||||
}
|
||||
|
||||
createAlivcLivePlayer(userData);
|
||||
|
||||
AlivcLivePlayer alivcLivePlayer = mInteractiveLivePlayerMap.get(userData.getKey());
|
||||
Log.d(TAG, "startPullRTCStream: " + userData.getKey() + ", " + userData.url + ", " + alivcLivePlayer);
|
||||
if (alivcLivePlayer != null) {
|
||||
alivcLivePlayer.startPlay(userData.url);
|
||||
}
|
||||
}
|
||||
|
||||
public void stopPullRTCStream(InteractiveUserData userData) {
|
||||
if (userData == null) {
|
||||
return;
|
||||
}
|
||||
String userKey = userData.getKey();
|
||||
AlivcLivePlayer alivcLivePlayer = mInteractiveLivePlayerMap.get(userKey);
|
||||
Log.d(TAG, "stopPullRTCStream: " + userData.getKey() + ", " + userData.url + ", " + alivcLivePlayer);
|
||||
if (alivcLivePlayer != null) {
|
||||
alivcLivePlayer.stopPlay();
|
||||
alivcLivePlayer.destroy();
|
||||
}
|
||||
mInteractiveLivePlayerMap.remove(userKey);
|
||||
mInteractiveUserDataMap.remove(userKey);
|
||||
}
|
||||
|
||||
// 暂停推流
|
||||
public void pausePush() {
|
||||
if (isPushing()) {
|
||||
mAlivcLivePusher.pause();
|
||||
}
|
||||
}
|
||||
|
||||
// 恢复推流
|
||||
public void resumePush() {
|
||||
if (isPushing()) {
|
||||
mAlivcLivePusher.resume();
|
||||
}
|
||||
}
|
||||
|
||||
// 停止播放RTC流
|
||||
public void pausePlayRTCStream(InteractiveUserData userData) {
|
||||
Log.d(TAG, "pausePlayRTCStream: " + userData);
|
||||
if (userData == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String userKey = userData.getKey();
|
||||
InteractiveLivePlayer alivcLivePlayer = mInteractiveLivePlayerMap.get(userKey);
|
||||
if (alivcLivePlayer == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
alivcLivePlayer.pauseVideoPlaying();
|
||||
}
|
||||
|
||||
// 恢复播放RTC流
|
||||
public void resumePlayRTCStream(InteractiveUserData userData) {
|
||||
Log.d(TAG, "resumePlayRTCStream: " + userData);
|
||||
if (userData == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String userKey = userData.getKey();
|
||||
InteractiveLivePlayer alivcLivePlayer = mInteractiveLivePlayerMap.get(userKey);
|
||||
if (alivcLivePlayer == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
alivcLivePlayer.resumeVideoPlaying();
|
||||
}
|
||||
|
||||
// 停止播放所有RTC流
|
||||
public void pausePlayRTCStream() {
|
||||
for (InteractiveUserData userData : mInteractiveUserDataMap.values()) {
|
||||
pausePlayRTCStream(userData);
|
||||
}
|
||||
}
|
||||
|
||||
// 恢复播放所有RTC流
|
||||
public void resumePlayRTCStream() {
|
||||
for (InteractiveUserData userData : mInteractiveUserDataMap.values()) {
|
||||
resumePlayRTCStream(userData);
|
||||
}
|
||||
}
|
||||
|
||||
// 发送自定义消息,发送SEI
|
||||
public void sendCustomMessage(String text, int payload) {
|
||||
if (LivePushGlobalConfig.IS_DATA_CHANNEL_MESSAGE_ENABLE) {
|
||||
mAlivcLivePusher.sendDataChannelMessage(text);
|
||||
} else {
|
||||
mAlivcLivePusher.sendMessage(text, 0, 0, false, payload);
|
||||
}
|
||||
}
|
||||
|
||||
// 开始本地录制
|
||||
public void startLocalRecord(AlivcLiveLocalRecordConfig recordConfig) {
|
||||
boolean flag = false;
|
||||
if (mAlivcLivePusher != null) {
|
||||
flag = mAlivcLivePusher.startLocalRecord(recordConfig);
|
||||
}
|
||||
if (!flag) {
|
||||
ToastUtils.show("start local record failed!");
|
||||
}
|
||||
}
|
||||
|
||||
// 结束本地录制
|
||||
public void stopLocalRecord() {
|
||||
if (mAlivcLivePusher != null) {
|
||||
mAlivcLivePusher.stopLocalRecord();
|
||||
}
|
||||
}
|
||||
|
||||
public void switchCamera() {
|
||||
if (isPushing()) {
|
||||
mAlivcLivePusher.switchCamera();
|
||||
if (mCameraId == Camera.CameraInfo.CAMERA_FACING_BACK) {
|
||||
mCameraId = Camera.CameraInfo.CAMERA_FACING_FRONT;
|
||||
} else {
|
||||
mCameraId = Camera.CameraInfo.CAMERA_FACING_BACK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setMute(boolean isMute) {
|
||||
if (mAlivcLivePusher != null) {
|
||||
mAlivcLivePusher.setMute(isMute);
|
||||
}
|
||||
}
|
||||
|
||||
public void setMute(boolean isMute, AlivcLiveMuteLocalAudioMode muteLocalAudioMode) {
|
||||
if (mAlivcLivePusher != null) {
|
||||
mAlivcLivePusher.setMute(isMute, muteLocalAudioMode);
|
||||
}
|
||||
}
|
||||
|
||||
public void setBGMEarsBack(boolean isOpen) {
|
||||
if (mAlivcLivePusher != null) {
|
||||
mAlivcLivePusher.setBGMEarsBack(isOpen);
|
||||
}
|
||||
}
|
||||
|
||||
public void setAudioEffectVoiceChangeMode(AlivcLivePushAudioEffectVoiceChangeMode voiceChangeMode) {
|
||||
if (mAlivcLivePusher != null) {
|
||||
mAlivcLivePusher.setAudioEffectVoiceChangeMode(voiceChangeMode);
|
||||
}
|
||||
}
|
||||
|
||||
public void setVideoConfig(AlivcLivePushVideoConfig videoConfig) {
|
||||
if (mAlivcLivePusher != null) {
|
||||
mAlivcLivePusher.setVideoConfig(videoConfig);
|
||||
}
|
||||
}
|
||||
|
||||
public void enableSpeakerPhone(boolean enableSpeakerPhone) {
|
||||
mAlivcLivePusher.enableSpeakerphone(enableSpeakerPhone);
|
||||
}
|
||||
|
||||
public void enableAudioCapture(boolean enable) {
|
||||
if (enable) {
|
||||
mAlivcLivePusher.startAudioCapture(true);
|
||||
} else {
|
||||
mAlivcLivePusher.stopAudioCapture();
|
||||
}
|
||||
}
|
||||
|
||||
public void muteLocalCamera(boolean muteLocalCamera) {
|
||||
if (mAlivcLivePusher != null) {
|
||||
mAlivcLivePusher.muteLocalCamera(muteLocalCamera);
|
||||
}
|
||||
}
|
||||
|
||||
public void enableLocalCamera(boolean enable) {
|
||||
if (mAlivcLivePusher != null) {
|
||||
mAlivcLivePusher.enableLocalCamera(enable);
|
||||
}
|
||||
}
|
||||
|
||||
public void changeResolution(AlivcResolutionEnum resolutionEnum) {
|
||||
if (mAlivcLivePusher != null) {
|
||||
mAlivcLivePusher.changeResolution(resolutionEnum);
|
||||
}
|
||||
}
|
||||
|
||||
public void release() {
|
||||
mSEIDelayManager.destroy();
|
||||
|
||||
// destroyWatermarks();
|
||||
clearLiveMixTranscodingConfig();
|
||||
|
||||
stopPush();
|
||||
destroyLivePusher();
|
||||
|
||||
mMultiInteractLiveMixStreamsArray.clear();
|
||||
mMixInteractLiveTranscodingConfig.mixStreams = mMultiInteractLiveMixStreamsArray;
|
||||
|
||||
for (AlivcLivePlayer alivcLivePlayer : mInteractiveLivePlayerMap.values()) {
|
||||
alivcLivePlayer.stopPlay();
|
||||
alivcLivePlayer.destroy();
|
||||
}
|
||||
|
||||
mInteractiveLivePlayerMap.clear();
|
||||
mInteractiveUserDataMap.clear();
|
||||
|
||||
mInteractLivePushPullListener = null;
|
||||
}
|
||||
|
||||
// 单切混的逻辑在各自场景的manager里面,因为PK和连麦的混流布局不太一样。
|
||||
// 更新混流、混切单
|
||||
public void clearLiveMixTranscodingConfig() {
|
||||
if (mAlivcLivePusher != null) {
|
||||
mAlivcLivePusher.setLiveMixTranscodingConfig(null);
|
||||
}
|
||||
}
|
||||
|
||||
// 遍历当前混流子布局信息
|
||||
public AlivcLiveMixStream findMixStreamByUserData(InteractiveUserData userData) {
|
||||
if (userData == null || TextUtils.isEmpty(userData.userId)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (AlivcLiveMixStream alivcLiveMixStream : mMultiInteractLiveMixStreamsArray) {
|
||||
if (userData.userId.equals(alivcLiveMixStream.userId)
|
||||
&& alivcLiveMixStream.mixSourceType == InteractiveBaseUtil.covertVideoStreamType2MixSourceType(userData.videoStreamType)) {
|
||||
return alivcLiveMixStream;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public InteractiveUserData getUserDataByKey(String key) {
|
||||
if (TextUtils.isEmpty(key)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (String userKey : mInteractiveUserDataMap.keySet()) {
|
||||
if (TextUtils.equals(userKey, key)) {
|
||||
return mInteractiveUserDataMap.get(userKey);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// 初始化美颜相关资源
|
||||
private void initBeautyManager() {
|
||||
if (mBeautyManager == null && LivePushGlobalConfig.ENABLE_BEAUTY) {
|
||||
// v4.4.4版本-v6.1.0版本,互动模式下的美颜,处理逻辑参考BeautySDKType.INTERACT_QUEEN,即:InteractQueenBeautyImpl;
|
||||
// v6.1.0以后的版本(从v6.2.0开始),基础模式下的美颜,和互动模式下的美颜,处理逻辑保持一致,即:QueenBeautyImpl;
|
||||
mBeautyManager = BeautyFactory.createBeauty(BeautySDKType.QUEEN, mContext);
|
||||
// initialize in texture thread.
|
||||
mBeautyManager.init();
|
||||
mBeautyManager.setBeautyEnable(LivePushGlobalConfig.ENABLE_BEAUTY);
|
||||
mBeautyManager.switchCameraId(mCameraId);
|
||||
}
|
||||
}
|
||||
|
||||
// 销毁美颜相关资源
|
||||
private void destroyBeautyManager() {
|
||||
if (mBeautyManager != null) {
|
||||
mBeautyManager.release();
|
||||
mBeautyManager = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ****************** 音频数据回调(参考示例) ****************** [START]
|
||||
* <p>
|
||||
* 调用建议:1. 调用时机任意;2. 注意清除逻辑
|
||||
*/
|
||||
private static final AlivcLiveAudioSource AUDIO_FRAME_OBSERVER_SOURCE = AlivcLiveAudioSource.PROCESS_CAPTURED;
|
||||
private static final AlivcLiveAudioFrameObserverOperationMode AUDIO_FRAME_OBSERVER_MODE = AlivcLiveAudioFrameObserverOperationMode.READ_WRITE;
|
||||
private static final AlivcLiveAudioFrameObserverUserDefinedInfoBitMask AUDIO_FRAME_OBSERVER_BIT_MASK = AlivcLiveAudioFrameObserverUserDefinedInfoBitMask.MIX_EXTERNAL_CAPTURE;
|
||||
|
||||
/**
|
||||
* 设置音频裸数据回调
|
||||
* <p>
|
||||
* 注意:该接口较耗时,会影响性能,如果不需要对音频裸数据进行前处理,建议关闭
|
||||
*/
|
||||
public void enableAudioFrameObserver(boolean enable) {
|
||||
if (mAlivcLivePusher == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (enable) {
|
||||
AlivcLiveAudioFrameObserverConfig audioFrameObserverConfig = new AlivcLiveAudioFrameObserverConfig();
|
||||
audioFrameObserverConfig.mode = AUDIO_FRAME_OBSERVER_MODE;
|
||||
audioFrameObserverConfig.userDefinedInfo = AUDIO_FRAME_OBSERVER_BIT_MASK;
|
||||
mAlivcLivePusher.enableAudioFrameObserver(true, AUDIO_FRAME_OBSERVER_SOURCE, audioFrameObserverConfig);
|
||||
mAlivcLivePusher.setLivePushAudioFrameListener(new AlivcLivePushAudioFrameListener() {
|
||||
@Override
|
||||
public boolean onCapturedAudioFrame(AlivcLivePushAudioFrame audioFrame) {
|
||||
// 设置 enableAudioFrameObserver 接口中的 AlivcLiveAudioSource 为 CAPTURED,会执行该回调
|
||||
// do audio process here, and then, return true value if audio data need to be rewrited
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onProcessCapturedAudioFrame(AlivcLivePushAudioFrame audioFrame) {
|
||||
// 设置 enableAudioFrameObserver 接口中的 AlivcLiveAudioSource 为 PROCESS_CAPTURED,会执行该回调
|
||||
// do audio process here, and then, return true value if audio data need to be rewrited
|
||||
return false;
|
||||
|
||||
// // 清空 byte buffer,测试是否生效
|
||||
// Arrays.fill(audioFrame.data, (byte) 0);
|
||||
// return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPublishAudioFrame(AlivcLivePushAudioFrame audioFrame) {
|
||||
// 设置 enableAudioFrameObserver 接口中的 AlivcLiveAudioSource 为 PUB,会执行该回调
|
||||
// do audio process here, and then, return true value if audio data need to be rewrited
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPlaybackAudioFrame(AlivcLivePushAudioFrame audioFrame) {
|
||||
// 设置 enableAudioFrameObserver 接口中的 AlivcLiveAudioSource 为 PLAYBACK,会执行该回调
|
||||
// do audio process here, and then, return true value if audio data need to be rewrited
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMixedAllAudioFrame(AlivcLivePushAudioFrame audioFrame) {
|
||||
// 设置 enableAudioFrameObserver 接口中的 AlivcLiveAudioSource 为 MIXED_ALL,会执行该回调
|
||||
// 注意:设置 AlivcLiveAudioSource.MIXED_ALL 时,只能读不能写
|
||||
// do audio process here, and then, return true value if audio data need to be rewrited
|
||||
return false;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// please ensure the callback unregistration to prevent memory leaks
|
||||
mAlivcLivePusher.enableAudioFrameObserver(false, AUDIO_FRAME_OBSERVER_SOURCE, null);
|
||||
mAlivcLivePusher.setLivePushAudioFrameListener(null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* [END] ****************** 音频数据回调(参考示例) ******************
|
||||
*/
|
||||
|
||||
/**
|
||||
* ****************** 音效接口(参考示例) ****************** [START]
|
||||
*/
|
||||
|
||||
// 音效文件 assets 路径
|
||||
private static final String SOUND_EFFECT_ASSETS_PATH = "alivc_resource/sound_effect/";
|
||||
|
||||
// 预加载音效
|
||||
public void loadAudioEffects() {
|
||||
if (mAlivcLivePusher == null || mContext == null) {
|
||||
return;
|
||||
}
|
||||
String soundEffectPath1 = mContext.getFilesDir().getPath() + File.separator + SOUND_EFFECT_ASSETS_PATH + "sound_effect_man_smile.mp3";
|
||||
String soundEffectPath2 = mContext.getFilesDir().getPath() + File.separator + SOUND_EFFECT_ASSETS_PATH + "sound_effect_roar.mp3";
|
||||
String soundEffectPath3 = mContext.getFilesDir().getPath() + File.separator + SOUND_EFFECT_ASSETS_PATH + "sound_effect_sinister_smile.mp3";
|
||||
mAlivcLivePusher.preloadAudioEffect(1, soundEffectPath1);
|
||||
mAlivcLivePusher.preloadAudioEffect(2, soundEffectPath2);
|
||||
mAlivcLivePusher.preloadAudioEffect(3, soundEffectPath3);
|
||||
}
|
||||
|
||||
// 取消预加载音效
|
||||
public void unloadAudioEffects() {
|
||||
if (mAlivcLivePusher == null || mContext == null) {
|
||||
return;
|
||||
}
|
||||
mAlivcLivePusher.unloadAudioEffect(1);
|
||||
mAlivcLivePusher.unloadAudioEffect(2);
|
||||
mAlivcLivePusher.unloadAudioEffect(3);
|
||||
}
|
||||
|
||||
// 播放音效
|
||||
public void playAudioEffects() {
|
||||
if (mAlivcLivePusher == null || mContext == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String soundEffectPath1 = mContext.getFilesDir().getPath() + File.separator + SOUND_EFFECT_ASSETS_PATH + "sound_effect_man_smile.mp3";
|
||||
String soundEffectPath2 = mContext.getFilesDir().getPath() + File.separator + SOUND_EFFECT_ASSETS_PATH + "sound_effect_roar.mp3";
|
||||
String soundEffectPath3 = mContext.getFilesDir().getPath() + File.separator + SOUND_EFFECT_ASSETS_PATH + "sound_effect_sinister_smile.mp3";
|
||||
|
||||
AlivcLiveAudioEffectConfig audioEffectConfig1 = new AlivcLiveAudioEffectConfig();
|
||||
audioEffectConfig1.needPublish = false;
|
||||
audioEffectConfig1.publishVolume = 0;
|
||||
audioEffectConfig1.playoutVolume = 0;
|
||||
|
||||
AlivcLiveAudioEffectConfig audioEffectConfig2 = new AlivcLiveAudioEffectConfig();
|
||||
audioEffectConfig2.needPublish = true;
|
||||
audioEffectConfig2.publishVolume = 100;
|
||||
audioEffectConfig2.playoutVolume = 100;
|
||||
|
||||
AlivcLiveAudioEffectConfig audioEffectConfig3 = new AlivcLiveAudioEffectConfig();
|
||||
audioEffectConfig3.needPublish = true;
|
||||
audioEffectConfig3.publishVolume = 100;
|
||||
audioEffectConfig3.playoutVolume = 100;
|
||||
audioEffectConfig3.loopCycles = 3;
|
||||
|
||||
mAlivcLivePusher.playAudioEffect(1, soundEffectPath1, audioEffectConfig1);
|
||||
mAlivcLivePusher.playAudioEffect(2, soundEffectPath2, audioEffectConfig2);
|
||||
mAlivcLivePusher.playAudioEffect(3, soundEffectPath3, audioEffectConfig3);
|
||||
}
|
||||
|
||||
public void stopAudioEffect() {
|
||||
if (mAlivcLivePusher == null) {
|
||||
return;
|
||||
}
|
||||
mAlivcLivePusher.stopAudioEffect(1);
|
||||
mAlivcLivePusher.stopAudioEffect(2);
|
||||
mAlivcLivePusher.stopAudioEffect(3);
|
||||
// mAlivcLivePusher.stopAllAudioEffects();
|
||||
}
|
||||
|
||||
public void pauseAudioEffect() {
|
||||
if (mAlivcLivePusher == null) {
|
||||
return;
|
||||
}
|
||||
mAlivcLivePusher.pauseAudioEffect(1);
|
||||
mAlivcLivePusher.pauseAudioEffect(2);
|
||||
mAlivcLivePusher.pauseAudioEffect(3);
|
||||
// mAlivcLivePusher.pauseAllAudioEffects();
|
||||
}
|
||||
|
||||
public void resumeAudioEffect() {
|
||||
if (mAlivcLivePusher == null) {
|
||||
return;
|
||||
}
|
||||
mAlivcLivePusher.resumeAudioEffect(1);
|
||||
mAlivcLivePusher.resumeAudioEffect(2);
|
||||
mAlivcLivePusher.resumeAudioEffect(3);
|
||||
// mAlivcLivePusher.resumeAllAudioEffects();
|
||||
}
|
||||
|
||||
/**
|
||||
* [END] ****************** 音效接口(参考示例) ******************
|
||||
*/
|
||||
|
||||
/**
|
||||
* ****************** 外部音视频输入/三方推流(参考示例) ****************** [START]
|
||||
*/
|
||||
// 新增外部音频流
|
||||
public int addExternalAudioStream(AlivcLivePushExternalAudioStreamConfig externalAudioStreamConfig) {
|
||||
if (mAlivcLivePusher != null) {
|
||||
return mAlivcLivePusher.addExternalAudioStream(externalAudioStreamConfig);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 删除外部音频流
|
||||
public int removeExternalAudioStream(int streamId) {
|
||||
if (mAlivcLivePusher != null) {
|
||||
return mAlivcLivePusher.removeExternalAudioStream(streamId);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 输入外部音频流数据
|
||||
public int pushExternalAudioStream(int streamId, AlivcLivePushAudioFrame audioFrame) {
|
||||
if (mAlivcLivePusher != null) {
|
||||
return mAlivcLivePusher.pushExternalAudioStream(streamId, audioFrame);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 设置是否与麦克风采集音频混合
|
||||
public int setMixedWithMic(boolean mixed) {
|
||||
if (mAlivcLivePusher != null) {
|
||||
return mAlivcLivePusher.setMixedWithMic(mixed);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 设置外部音频流播放音量
|
||||
public int setExternalAudioStreamPlayoutVolume(int streamId, int playoutVolume) {
|
||||
if (mAlivcLivePusher != null) {
|
||||
return mAlivcLivePusher.setExternalAudioStreamPlayoutVolume(streamId, playoutVolume);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 设置外部音频流推流音量
|
||||
public int setExternalAudioStreamPublishVolume(int streamId, int publishVolume) {
|
||||
if (mAlivcLivePusher != null) {
|
||||
return mAlivcLivePusher.setExternalAudioStreamPublishVolume(streamId, publishVolume);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 启用外部视频输入源
|
||||
public void setExternalVideoSource(boolean enable, boolean useTexture, AlivcLivePlayVideoStreamType videoStreamType, AlivcPreviewDisplayMode previewDisplayMode) {
|
||||
if (mAlivcLivePusher != null) {
|
||||
mAlivcLivePusher.setExternalVideoSource(enable, useTexture, videoStreamType, previewDisplayMode);
|
||||
}
|
||||
}
|
||||
|
||||
// 外部视频输入/三方推流
|
||||
public void inputStreamVideoData(byte[] data, int width, int height, int stride, int size, long pts, int rotation) {
|
||||
if (mAlivcLivePusher != null) {
|
||||
mAlivcLivePusher.inputStreamVideoData(data, width, height, stride, size, pts, rotation);
|
||||
}
|
||||
}
|
||||
|
||||
// 外部音频输入/三方推流
|
||||
public void inputStreamAudioData(byte[] data, int size, int sampleRate, int channels, long pts) {
|
||||
if (mAlivcLivePusher != null) {
|
||||
mAlivcLivePusher.inputStreamAudioData(data, size, sampleRate, channels, pts);
|
||||
}
|
||||
}
|
||||
|
||||
// 输入外部视频流数据
|
||||
public void pushExternalVideoFrame(AlivcLivePusherRawDataSample rawDataSample, AlivcLivePlayVideoStreamType videoStreamType) {
|
||||
if (mAlivcLivePusher != null) {
|
||||
mAlivcLivePusher.pushExternalVideoFrame(rawDataSample, videoStreamType);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* [END] ****************** 外部音视频输入/三方推流(参考示例) ******************
|
||||
*/
|
||||
|
||||
/**
|
||||
* ****************** 视频双流(参考示例) ****************** [START]
|
||||
*/
|
||||
// 开始屏幕共享流
|
||||
public int startScreenShare() {
|
||||
if (mAlivcLivePusher != null) {
|
||||
return mAlivcLivePusher.startScreenShare();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 停止屏幕共享流
|
||||
public int stopScreenShare() {
|
||||
if (mAlivcLivePusher != null) {
|
||||
return mAlivcLivePusher.stopScreenShare();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* [END] ****************** 视频双流(参考示例) ******************
|
||||
*/
|
||||
|
||||
/**
|
||||
* ****************** 音频双流(参考示例) ****************** [START]
|
||||
*/
|
||||
// 开启第二路音频流推送
|
||||
public int startDualAudioStream() {
|
||||
if (mAlivcLivePusher != null) {
|
||||
return mAlivcLivePusher.startLocalDualAudioStream();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 停止第二路音频流推送
|
||||
public int stopDualAudioStream() {
|
||||
if (mAlivcLivePusher != null) {
|
||||
return mAlivcLivePusher.stopLocalDualAudioStream();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* [END] ****************** 音频双流(参考示例) ******************
|
||||
*/
|
||||
}
|
||||
@@ -0,0 +1,241 @@
|
||||
package com.alivc.live.interactive_common;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.Color;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.text.TextUtils;
|
||||
import android.text.TextWatcher;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.acker.simplezxing.activity.CaptureActivity;
|
||||
import com.alibaba.android.arouter.launcher.ARouter;
|
||||
import com.alivc.live.commonbiz.SharedPreferenceUtils;
|
||||
import com.alivc.live.commonui.utils.StatusBarUtil;
|
||||
|
||||
public class InteractiveAppInfoActivity extends AppCompatActivity {
|
||||
|
||||
public static final String FROM_EDITOR = "from_editor";
|
||||
private static final int REQ_CODE_PERMISSION = 0x1111;
|
||||
|
||||
private ImageView mBackImageView;
|
||||
private EditText mAppIdEditText;
|
||||
private EditText mAppKeyEditText;
|
||||
private EditText mPlayDomainEditText;
|
||||
private TextView mConfirmTextView;
|
||||
private ImageView mAppIdClearImageView;
|
||||
private ImageView mAppKeyClearImageView;
|
||||
private ImageView mPlayDomainClearImageView;
|
||||
|
||||
private boolean mFromEditor = false;
|
||||
private int mSceneType = InteractiveConstants.SCENE_TYPE_INTERACTIVE_LIVE;
|
||||
private EditText mCurrentEditText = null;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
StatusBarUtil.translucent(this, Color.TRANSPARENT);
|
||||
|
||||
setContentView(R.layout.activity_interactive_app_info);
|
||||
|
||||
mFromEditor = getIntent().getBooleanExtra(FROM_EDITOR, false);
|
||||
mSceneType = getIntent().getIntExtra(InteractiveConstants.DATA_TYPE_SCENE, InteractiveConstants.SCENE_TYPE_INTERACTIVE_LIVE);
|
||||
|
||||
initView();
|
||||
initListener();
|
||||
}
|
||||
|
||||
private void initView() {
|
||||
mBackImageView = findViewById(R.id.iv_back);
|
||||
|
||||
mAppIdEditText = findViewById(R.id.et_app_id);
|
||||
mAppKeyEditText = findViewById(R.id.et_app_key);
|
||||
mPlayDomainEditText = findViewById(R.id.et_play_domain);
|
||||
|
||||
mAppIdClearImageView = findViewById(R.id.iv_app_id_clear);
|
||||
mAppKeyClearImageView = findViewById(R.id.iv_app_key_clear);
|
||||
mPlayDomainClearImageView = findViewById(R.id.iv_play_domain_clear);
|
||||
|
||||
mConfirmTextView = findViewById(R.id.tv_confirm);
|
||||
}
|
||||
|
||||
private void initListener() {
|
||||
mBackImageView.setOnClickListener((view) -> finish());
|
||||
|
||||
mAppIdEditText.addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable editable) {
|
||||
mAppIdClearImageView.setImageResource(TextUtils.isEmpty(editable.toString()) ? R.drawable.ic_scan_icon : R.drawable.ic_close);
|
||||
changeConfirmTextView(checkEnable());
|
||||
}
|
||||
});
|
||||
|
||||
mAppKeyEditText.addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable editable) {
|
||||
mAppKeyClearImageView.setImageResource(TextUtils.isEmpty(editable.toString()) ? R.drawable.ic_scan_icon : R.drawable.ic_close);
|
||||
changeConfirmTextView(checkEnable());
|
||||
}
|
||||
});
|
||||
|
||||
mPlayDomainEditText.addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable editable) {
|
||||
mPlayDomainClearImageView.setImageResource(TextUtils.isEmpty(editable.toString()) ? R.drawable.ic_scan_icon : R.drawable.ic_close);
|
||||
changeConfirmTextView(checkEnable());
|
||||
}
|
||||
});
|
||||
|
||||
mAppIdClearImageView.setOnClickListener((view) -> checkForQrOrClear(mAppIdEditText));
|
||||
mAppKeyClearImageView.setOnClickListener((view) -> checkForQrOrClear(mAppKeyEditText));
|
||||
mPlayDomainClearImageView.setOnClickListener((view) -> checkForQrOrClear(mPlayDomainEditText));
|
||||
|
||||
mConfirmTextView.setOnClickListener((view) -> {
|
||||
if (checkEnable()) {
|
||||
String appId = mAppIdEditText.getText().toString();
|
||||
String appKey = mAppKeyEditText.getText().toString();
|
||||
String playDomain = mPlayDomainEditText.getText().toString();
|
||||
SharedPreferenceUtils.setAppInfo(getApplicationContext(), appId, appKey, playDomain);
|
||||
if (!mFromEditor) {
|
||||
ARouter.getInstance()
|
||||
.build("/interactiveCommon/liveInput")
|
||||
.withInt(InteractiveConstants.DATA_TYPE_SCENE, mSceneType)
|
||||
.navigation();
|
||||
}
|
||||
finish();
|
||||
}
|
||||
});
|
||||
|
||||
mAppIdEditText.setText(SharedPreferenceUtils.getAppId(getApplicationContext().getApplicationContext()));
|
||||
mAppKeyEditText.setText(SharedPreferenceUtils.getAppKey(getApplicationContext().getApplicationContext()));
|
||||
mPlayDomainEditText.setText(SharedPreferenceUtils.getPlayDomain(getApplicationContext().getApplicationContext()));
|
||||
}
|
||||
|
||||
private void checkForQrOrClear(EditText editText) {
|
||||
String content = editText.getText().toString().trim();
|
||||
if (TextUtils.isEmpty(content)) {
|
||||
mCurrentEditText = editText;
|
||||
startQr();
|
||||
} else {
|
||||
editText.setText("");
|
||||
}
|
||||
}
|
||||
|
||||
private void changeConfirmTextView(boolean enable) {
|
||||
if (enable) {
|
||||
mConfirmTextView.setBackground(getResources().getDrawable(R.drawable.shape_pysh_btn_bg));
|
||||
} else {
|
||||
mConfirmTextView.setBackground(getResources().getDrawable(R.drawable.shape_rect_blue));
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkEnable() {
|
||||
String appId = mAppIdEditText.getText().toString();
|
||||
String appKey = mAppKeyEditText.getText().toString();
|
||||
String playDomain = mPlayDomainEditText.getText().toString();
|
||||
return !TextUtils.isEmpty(appId) && !TextUtils.isEmpty(appKey) && !TextUtils.isEmpty(playDomain);
|
||||
}
|
||||
|
||||
private void startQr() {
|
||||
if (ContextCompat.checkSelfPermission(InteractiveAppInfoActivity.this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
|
||||
// Do not have the permission of camera, request it.
|
||||
ActivityCompat.requestPermissions(InteractiveAppInfoActivity.this, new String[]{Manifest.permission.CAMERA}, REQ_CODE_PERMISSION);
|
||||
} else {
|
||||
// Have gotten the permission
|
||||
startCaptureActivityForResult();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
switch (requestCode) {
|
||||
case REQ_CODE_PERMISSION: {
|
||||
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
// User agree the permission
|
||||
startCaptureActivityForResult();
|
||||
} else {
|
||||
// User disagree the permission
|
||||
Toast.makeText(this, "You must agree the camera permission request before you use the code scan function", Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
switch (requestCode) {
|
||||
case CaptureActivity.REQ_CODE:
|
||||
switch (resultCode) {
|
||||
case RESULT_OK:
|
||||
if (mCurrentEditText != null) {
|
||||
mCurrentEditText.setText(data.getStringExtra(CaptureActivity.EXTRA_SCAN_RESULT));
|
||||
}
|
||||
break;
|
||||
case RESULT_CANCELED:
|
||||
if (data != null && mCurrentEditText != null) {
|
||||
// for some reason camera is not working correctly
|
||||
mCurrentEditText.setText(data.getStringExtra(CaptureActivity.EXTRA_SCAN_RESULT));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void startCaptureActivityForResult() {
|
||||
Intent intent = new Intent(InteractiveAppInfoActivity.this, CaptureActivity.class);
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putBoolean(CaptureActivity.KEY_NEED_BEEP, CaptureActivity.VALUE_BEEP);
|
||||
bundle.putBoolean(CaptureActivity.KEY_NEED_VIBRATION, CaptureActivity.VALUE_VIBRATION);
|
||||
bundle.putBoolean(CaptureActivity.KEY_NEED_EXPOSURE, CaptureActivity.VALUE_NO_EXPOSURE);
|
||||
bundle.putByte(CaptureActivity.KEY_FLASHLIGHT_MODE, CaptureActivity.VALUE_FLASHLIGHT_OFF);
|
||||
bundle.putByte(CaptureActivity.KEY_ORIENTATION_MODE, CaptureActivity.VALUE_ORIENTATION_AUTO);
|
||||
bundle.putBoolean(CaptureActivity.KEY_SCAN_AREA_FULL_SCREEN, CaptureActivity.VALUE_SCAN_AREA_FULL_SCREEN);
|
||||
bundle.putBoolean(CaptureActivity.KEY_NEED_SCAN_HINT_TEXT, CaptureActivity.VALUE_SCAN_HINT_TEXT);
|
||||
intent.putExtra(CaptureActivity.EXTRA_SETTING_BUNDLE, bundle);
|
||||
startActivityForResult(intent, CaptureActivity.REQ_CODE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.alivc.live.interactive_common;
|
||||
|
||||
import com.alivc.live.player.annotations.AlivcLivePlayVideoStreamType;
|
||||
import com.alivc.live.pusher.AlivcLiveMixSourceType;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* @author keria
|
||||
* @date 2023/10/30
|
||||
* @brief
|
||||
*/
|
||||
public class InteractiveBaseUtil {
|
||||
|
||||
private InteractiveBaseUtil() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成随机房间号和用户id
|
||||
*
|
||||
* @param sceneType 场景;直播连麦场景随机数0-100,PK场景随机数100-999,以此区分
|
||||
* @param listener 回调结果
|
||||
*/
|
||||
public static void generateRandomId(int sceneType, OnRandomIdListener listener) {
|
||||
Random random = new Random();
|
||||
int randomNumber;
|
||||
|
||||
String channelId;
|
||||
String userId;
|
||||
if (sceneType == InteractiveConstants.SCENE_TYPE_INTERACTIVE_LIVE) {
|
||||
randomNumber = random.nextInt(99) + 1;
|
||||
String randomNumberStr = String.valueOf(randomNumber);
|
||||
userId = randomNumberStr; // 互动直播的场景,用户ID随机数范围 0-99
|
||||
channelId = randomNumberStr; // 互动直播的场景,房间ID随机数范围 0-99
|
||||
} else {
|
||||
randomNumber = random.nextInt(900) + 100; // 非互动直播的场景,随机数范围 0-999
|
||||
String randomNumberStr = String.valueOf(randomNumber);
|
||||
channelId = randomNumberStr;// 设置房间ID
|
||||
userId = randomNumberStr;// 设置用户ID,与房间ID相同
|
||||
}
|
||||
|
||||
if (listener != null) {
|
||||
listener.onResult(channelId, userId);
|
||||
}
|
||||
}
|
||||
|
||||
public static AlivcLiveMixSourceType covertVideoStreamType2MixSourceType(AlivcLivePlayVideoStreamType videoStreamType) {
|
||||
if (videoStreamType == AlivcLivePlayVideoStreamType.STREAM_SCREEN) {
|
||||
return AlivcLiveMixSourceType.SOURCE_SCREEN;
|
||||
}
|
||||
return AlivcLiveMixSourceType.SOURCE_CAMERA;
|
||||
}
|
||||
|
||||
public interface OnRandomIdListener {
|
||||
void onResult(String channelId, String userId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.alivc.live.interactive_common;
|
||||
|
||||
/**
|
||||
* @author keria
|
||||
* @date 2024/5/29
|
||||
* @brief 互动直播常量类
|
||||
*/
|
||||
public @interface InteractiveConstants {
|
||||
String DATA_TYPE_INTERACTIVE_USER_DATA = "user_data";
|
||||
String DATA_TYPE_IS_ANCHOR = "IS_ANCHOR";
|
||||
String DATA_TYPE_SCENE = "SCENE";
|
||||
|
||||
String KEY_TYPE_SCENE_MULTI_PK_16IN = "scene_multi_pk_16in";
|
||||
|
||||
int SCENE_TYPE_INTERACTIVE_LIVE = 0;
|
||||
int SCENE_TYPE_PK_LIVE = 1;
|
||||
}
|
||||
@@ -0,0 +1,294 @@
|
||||
package com.alivc.live.interactive_common;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.graphics.Color;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.text.TextUtils;
|
||||
import android.text.TextWatcher;
|
||||
import android.view.View;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import com.acker.simplezxing.activity.CaptureActivity;
|
||||
import com.alibaba.android.arouter.facade.annotation.Route;
|
||||
import com.alibaba.android.arouter.launcher.ARouter;
|
||||
import com.alivc.live.commonbiz.backdoor.BackDoorInstance;
|
||||
import com.alivc.live.commonbiz.test.AliLiveStreamURLUtil;
|
||||
import com.alivc.live.commonui.utils.StatusBarUtil;
|
||||
import com.alivc.live.commonutils.TextFormatUtil;
|
||||
import com.alivc.live.commonutils.ToastUtils;
|
||||
import com.alivc.live.interactive_common.bean.InteractiveUserData;
|
||||
import com.alivc.live.interactive_common.utils.LivePushGlobalConfig;
|
||||
import com.alivc.live.interactive_common.widget.InteractLiveRadioButton;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* @author keria
|
||||
* @date 2024/5/29
|
||||
* @brief 互动直播房间号/用户ID输入页面
|
||||
*/
|
||||
@Route(path = "/interactiveCommon/liveInput")
|
||||
public class InteractiveInputActivity extends AppCompatActivity {
|
||||
|
||||
private EditText mUserIdEdt;
|
||||
private EditText mRoomIdEdt;
|
||||
|
||||
private ImageView mUserIdClearIv;
|
||||
private ImageView mRoomIdClearIv;
|
||||
|
||||
private TextView mURLEntranceTv;
|
||||
private TextView mInteractiveURLTv;
|
||||
|
||||
private TextView mSettingTv;
|
||||
private TextView mConfirmTv;
|
||||
private ImageView mBackIv;
|
||||
|
||||
// 直播连麦场景下的主播/观众角色选取
|
||||
private InteractLiveRadioButton mAnchorRb;
|
||||
private InteractLiveRadioButton mAudienceRb;
|
||||
|
||||
// 场景类型,PK直播 or 连麦直播
|
||||
private int mSceneType = InteractiveConstants.SCENE_TYPE_INTERACTIVE_LIVE;
|
||||
private boolean isAnchor = true;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
StatusBarUtil.translucent(this, Color.TRANSPARENT);
|
||||
|
||||
setContentView(R.layout.activity_interactive_input);
|
||||
|
||||
// 获取场景类型
|
||||
mSceneType = getIntent().getIntExtra(InteractiveConstants.DATA_TYPE_SCENE, InteractiveConstants.SCENE_TYPE_INTERACTIVE_LIVE);
|
||||
|
||||
initView();
|
||||
initListener();
|
||||
}
|
||||
|
||||
private void initView() {
|
||||
mUserIdEdt = findViewById(R.id.et_user_id);
|
||||
mRoomIdEdt = findViewById(R.id.et_room_id);
|
||||
mUserIdClearIv = findViewById(R.id.iv_user_id_clear);
|
||||
mRoomIdClearIv = findViewById(R.id.iv_room_id_clear);
|
||||
|
||||
mURLEntranceTv = findViewById(R.id.tv_url_entrance);
|
||||
mInteractiveURLTv = findViewById(R.id.tv_interactive_url);
|
||||
|
||||
// Update page title
|
||||
TextView titleTv = findViewById(R.id.tv_title);
|
||||
titleTv.setText(mSceneType == InteractiveConstants.SCENE_TYPE_PK_LIVE ? getString(R.string.pk_live) : getString(R.string.interact_live));
|
||||
|
||||
mSettingTv = findViewById(R.id.tv_setting);
|
||||
mBackIv = findViewById(R.id.iv_back);
|
||||
mConfirmTv = findViewById(R.id.tv_confirm);
|
||||
|
||||
View roleContainer = findViewById(R.id.role_container);
|
||||
roleContainer.setVisibility(mSceneType == InteractiveConstants.SCENE_TYPE_PK_LIVE ? View.GONE : View.VISIBLE);
|
||||
|
||||
mAnchorRb = findViewById(R.id.radio_button_anchor);
|
||||
mAnchorRb.setChecked(true);
|
||||
mAudienceRb = findViewById(R.id.radio_button_audience);
|
||||
}
|
||||
|
||||
private void initListener() {
|
||||
InteractLiveAppInfoFragment interactLiveAppInfoFragment = (InteractLiveAppInfoFragment) getSupportFragmentManager().findFragmentById(R.id.fragment_app_info);
|
||||
if (interactLiveAppInfoFragment != null) {
|
||||
interactLiveAppInfoFragment.setOnEditClickListener(new InteractLiveAppInfoFragment.OnEditClickListener() {
|
||||
@Override
|
||||
public void onLongClickAppInfoForDebug() {
|
||||
InteractiveBaseUtil.generateRandomId(mSceneType, (channelId, userId) -> {
|
||||
if (TextUtils.isEmpty(mRoomIdEdt.getText())) {
|
||||
mRoomIdEdt.setText(channelId);
|
||||
}
|
||||
if (TextUtils.isEmpty(mUserIdEdt.getText())) {
|
||||
mUserIdEdt.setText(userId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEditClickListener() {
|
||||
Intent intent = new Intent(InteractiveInputActivity.this, InteractiveAppInfoActivity.class);
|
||||
intent.putExtra(InteractiveAppInfoActivity.FROM_EDITOR, true);
|
||||
intent.putExtra(InteractiveConstants.DATA_TYPE_SCENE, mSceneType);
|
||||
startActivity(intent);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
mBackIv.setOnClickListener(view -> finish());
|
||||
|
||||
mURLEntranceTv.setOnClickListener(v -> {
|
||||
startCaptureActivityForResult();
|
||||
});
|
||||
|
||||
mSettingTv.setOnClickListener(view -> {
|
||||
Intent intent = new Intent(this, InteractiveSettingActivity.class);
|
||||
startActivity(intent);
|
||||
});
|
||||
|
||||
mUserIdEdt.addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable editable) {
|
||||
changeConfirmTextView(checkEnable());
|
||||
}
|
||||
});
|
||||
|
||||
mRoomIdEdt.addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable editable) {
|
||||
changeConfirmTextView(checkEnable());
|
||||
}
|
||||
});
|
||||
|
||||
mAnchorRb.setOnClickListener(view -> {
|
||||
isAnchor = true;
|
||||
mAnchorRb.setChecked(true);
|
||||
mAudienceRb.setChecked(false);
|
||||
});
|
||||
|
||||
mAudienceRb.setOnClickListener(view -> {
|
||||
isAnchor = false;
|
||||
mAnchorRb.setChecked(false);
|
||||
mAudienceRb.setChecked(true);
|
||||
});
|
||||
|
||||
mUserIdClearIv.setOnClickListener(view -> mUserIdEdt.setText(""));
|
||||
mRoomIdClearIv.setOnClickListener(view -> mRoomIdEdt.setText(""));
|
||||
|
||||
mConfirmTv.setOnClickListener(view -> {
|
||||
if (checkEnable()) {
|
||||
startInteractiveActivity();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void changeConfirmTextView(boolean enable) {
|
||||
if (enable) {
|
||||
mConfirmTv.setBackground(getResources().getDrawable(R.drawable.shape_pysh_btn_bg));
|
||||
} else {
|
||||
mConfirmTv.setBackground(getResources().getDrawable(R.drawable.shape_rect_blue));
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkEnable() {
|
||||
String url = mInteractiveURLTv.getText().toString();
|
||||
if (!TextUtils.isEmpty(url)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
String userId = mUserIdEdt.getText().toString();
|
||||
String roomId = mRoomIdEdt.getText().toString();
|
||||
return !TextUtils.isEmpty(userId) && !TextUtils.isEmpty(roomId) && Pattern.matches(TextFormatUtil.REGULAR, userId) && Pattern.matches(TextFormatUtil.REGULAR, roomId);
|
||||
}
|
||||
|
||||
private void startInteractiveActivity() {
|
||||
String schema;
|
||||
if (mSceneType == InteractiveConstants.SCENE_TYPE_PK_LIVE) {
|
||||
schema = LivePushGlobalConfig.IS_MULTI_INTERACT
|
||||
? "/interactivePK/multiPKLive"
|
||||
: "/interactivePK/pkLive";
|
||||
} else {
|
||||
schema = LivePushGlobalConfig.IS_MULTI_INTERACT
|
||||
? "/interactiveLive/multiInteractLive"
|
||||
: "/interactiveLive/interactLive";
|
||||
}
|
||||
|
||||
ARouter.getInstance()
|
||||
.build(schema)
|
||||
.withSerializable(InteractiveConstants.DATA_TYPE_INTERACTIVE_USER_DATA, generateUserData())
|
||||
.withBoolean(InteractiveConstants.DATA_TYPE_IS_ANCHOR, isAnchor)
|
||||
.withBoolean(InteractiveConstants.KEY_TYPE_SCENE_MULTI_PK_16IN, BackDoorInstance.getInstance().isUseMultiPK16IN())
|
||||
.navigation();
|
||||
}
|
||||
|
||||
private void startCaptureActivityForResult() {
|
||||
Intent intent = new Intent(InteractiveInputActivity.this, CaptureActivity.class);
|
||||
startActivityForResult(intent, CaptureActivity.REQ_CODE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
if (requestCode == CaptureActivity.REQ_CODE) {
|
||||
if (resultCode == RESULT_OK) {
|
||||
String pushUrl = data.getStringExtra(CaptureActivity.EXTRA_SCAN_RESULT);
|
||||
if (TextUtils.isEmpty(pushUrl)) {
|
||||
return;
|
||||
}
|
||||
mInteractiveURLTv.setText(pushUrl);
|
||||
clearInputUserInfo(true);
|
||||
changeConfirmTextView(checkEnable());
|
||||
checkPushURLSDKAppID(pushUrl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 清除输入的用户信息
|
||||
private void clearInputUserInfo(boolean useUrl) {
|
||||
if (useUrl) {
|
||||
mUserIdEdt.setText("");
|
||||
mRoomIdEdt.setText("");
|
||||
} else {
|
||||
mInteractiveURLTv.setText("");
|
||||
}
|
||||
}
|
||||
|
||||
// 如果当前以URL方式输入的 sdkAppId 和 demo 当前环境使用的 sdkAppId 不一致,则提示 toast,以免业务异常
|
||||
private void checkPushURLSDKAppID(String pushURL) {
|
||||
if (TextUtils.isEmpty(pushURL)) {
|
||||
return;
|
||||
}
|
||||
HashMap<String, String> params = AliLiveStreamURLUtil.parseUrl(pushURL);
|
||||
String sdkAppId = params.get(AliLiveStreamURLUtil.SDK_APP_ID);
|
||||
if (!TextUtils.equals(sdkAppId, AliLiveStreamURLUtil.APP_ID)) {
|
||||
ToastUtils.show(getString(R.string.tips_interactive_url_use_different_sdkappid));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成 user data,里面存有房间号,用户id,实际推流地址等信息
|
||||
*
|
||||
* @return user data
|
||||
*/
|
||||
private InteractiveUserData generateUserData() {
|
||||
String channelId = mRoomIdEdt.getText().toString();
|
||||
String userId = mUserIdEdt.getText().toString();
|
||||
String pushURL = mInteractiveURLTv.getText().toString();
|
||||
|
||||
InteractiveUserData userData = new InteractiveUserData();
|
||||
if (!TextUtils.isEmpty(channelId) && !TextUtils.isEmpty(userId)) {
|
||||
userData.userId = userId;
|
||||
userData.channelId = channelId;
|
||||
userData.url = AliLiveStreamURLUtil.generateInteractivePushUrl(channelId, userId);
|
||||
} else if (!TextUtils.isEmpty(pushURL)) {
|
||||
HashMap<String, String> params = AliLiveStreamURLUtil.parseUrl(pushURL);
|
||||
userData.channelId = AliLiveStreamURLUtil.parseURLStreamName(params);
|
||||
userData.userId = params.get(AliLiveStreamURLUtil.USER_ID);
|
||||
userData.url = pushURL;
|
||||
}
|
||||
return userData;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.alivc.live.interactive_common;
|
||||
|
||||
/**
|
||||
* @author keria
|
||||
* @date 2023/10/27
|
||||
* @brief DEMO互动模式业务类型
|
||||
*/
|
||||
public enum InteractiveMode {
|
||||
|
||||
/**
|
||||
* 1v1连麦
|
||||
*/
|
||||
INTERACTIVE(0x10),
|
||||
|
||||
/**
|
||||
* 多人连麦
|
||||
*/
|
||||
MULTI_INTERACTIVE(0x11),
|
||||
|
||||
/**
|
||||
* 1v1 PK
|
||||
*/
|
||||
PK(0x20),
|
||||
|
||||
/**
|
||||
* 多人PK
|
||||
*/
|
||||
MULTI_PK(0x21),
|
||||
|
||||
/**
|
||||
* 1v1推拉裸流
|
||||
*/
|
||||
BARE_STREAM(0x30),
|
||||
|
||||
/**
|
||||
* 多人推拉裸流
|
||||
*/
|
||||
MULTI_BARE_STREAM(0x31),
|
||||
;
|
||||
|
||||
private final int mode;
|
||||
|
||||
InteractiveMode(int mode) {
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否是直播连麦(先推后拉)
|
||||
*
|
||||
* @param interactiveMode 互动模式业务类型
|
||||
* @return true->是,false->否
|
||||
*/
|
||||
public static boolean isInteractive(InteractiveMode interactiveMode) {
|
||||
return interactiveMode == INTERACTIVE
|
||||
|| interactiveMode == MULTI_INTERACTIVE
|
||||
|| interactiveMode == PK
|
||||
|| interactiveMode == MULTI_PK;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否是推拉裸流(直推直拉)
|
||||
*
|
||||
* @param interactiveMode 互动模式业务类型
|
||||
* @return true->是,false->否
|
||||
*/
|
||||
public static boolean isBareStream(InteractiveMode interactiveMode) {
|
||||
return interactiveMode == BARE_STREAM
|
||||
|| interactiveMode == MULTI_BARE_STREAM;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,350 @@
|
||||
package com.alivc.live.interactive_common;
|
||||
|
||||
import static com.alivc.live.interactive_common.utils.LivePushGlobalConfig.mAlivcLivePushConfig;
|
||||
|
||||
import android.app.DownloadManager;
|
||||
import android.graphics.Color;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.Switch;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import com.alivc.live.annotations.AlivcLiveCameraCaptureOutputPreference;
|
||||
import com.alivc.live.commonbiz.ResourcesDownload;
|
||||
import com.alivc.live.commonbiz.SharedPreferenceUtils;
|
||||
import com.alivc.live.commonui.configview.LivePushSettingView;
|
||||
import com.alivc.live.commonui.utils.StatusBarUtil;
|
||||
import com.alivc.live.commonui.widgets.AVLiveLoadingDialog;
|
||||
import com.alivc.live.commonutils.DownloadUtil;
|
||||
import com.alivc.live.commonutils.ToastUtils;
|
||||
import com.alivc.live.interactive_common.utils.LivePushGlobalConfig;
|
||||
import com.alivc.live.pusher.AlivcAudioChannelEnum;
|
||||
import com.alivc.live.pusher.AlivcAudioSampleRateEnum;
|
||||
import com.alivc.live.pusher.AlivcAudioSceneModeEnum;
|
||||
import com.alivc.live.pusher.AlivcEncodeModeEnum;
|
||||
import com.alivc.live.pusher.AlivcImageFormat;
|
||||
import com.alivc.live.pusher.AlivcLivePushCameraTypeEnum;
|
||||
import com.alivc.live.pusher.AlivcPreviewOrientationEnum;
|
||||
import com.alivc.live.pusher.AlivcResolutionEnum;
|
||||
import com.alivc.live.pusher.AlivcSoundFormat;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class InteractiveSettingActivity extends AppCompatActivity {
|
||||
|
||||
private View mTabArgsLayout;
|
||||
private View mTabActionLayout;
|
||||
private View mTabArgsView;
|
||||
private View mTabActionView;
|
||||
private ImageView mBackImageView;
|
||||
private Switch mMultiInteractSwitch;
|
||||
private Switch mH5CompatibleSwitch;
|
||||
private Switch mDataChannelSwitch;
|
||||
private Switch mEarbackOpenWithoutHeadsetSwitch;
|
||||
private TextView mCameraCaptureOutputPreferenceTv;
|
||||
private Button mCommitButton;
|
||||
private AVLiveLoadingDialog mLoadingDialog;
|
||||
|
||||
private LivePushSettingView mLivePushSettingView;
|
||||
private AlivcResolutionEnum mCurrentResolution;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
StatusBarUtil.translucent(this, Color.TRANSPARENT);
|
||||
|
||||
setContentView(R.layout.activity_interactive_setting);
|
||||
|
||||
initView();
|
||||
initData();
|
||||
initListener();
|
||||
}
|
||||
|
||||
private void initView() {
|
||||
mBackImageView = findViewById(R.id.iv_back);
|
||||
mCommitButton = findViewById(R.id.btn_commit);
|
||||
mMultiInteractSwitch = findViewById(R.id.multi_interaction_control);
|
||||
mMultiInteractSwitch.setChecked(LivePushGlobalConfig.IS_MULTI_INTERACT);
|
||||
mH5CompatibleSwitch = findViewById(R.id.h5_compatible_control);
|
||||
mH5CompatibleSwitch.setChecked(LivePushGlobalConfig.IS_H5_COMPATIBLE);
|
||||
mDataChannelSwitch = findViewById(R.id.data_channel_control);
|
||||
mDataChannelSwitch.setChecked(LivePushGlobalConfig.IS_DATA_CHANNEL_MESSAGE_ENABLE);
|
||||
|
||||
mEarbackOpenWithoutHeadsetSwitch = findViewById(R.id.earback_open_without_headset_control);
|
||||
mEarbackOpenWithoutHeadsetSwitch.setChecked(LivePushGlobalConfig.IS_EARBACK_OPEN_WITHOUT_HEADSET);
|
||||
|
||||
mCameraCaptureOutputPreferenceTv = findViewById(R.id.setting_camera_capture_output_preference);
|
||||
String cameraCaptureOutputPreferenceText = getTextFromCameraCaptureOutputPreference(LivePushGlobalConfig.CAMERA_CAPTURE_OUTPUT_PREFERENCE);
|
||||
mCameraCaptureOutputPreferenceTv.setText(cameraCaptureOutputPreferenceText);
|
||||
|
||||
mTabArgsLayout = findViewById(R.id.tab_args_layout);
|
||||
mTabActionLayout = findViewById(R.id.tab_action_layout);
|
||||
mTabArgsView = findViewById(R.id.tab_args_view);
|
||||
mTabActionView = findViewById(R.id.tab_action_view);
|
||||
mLivePushSettingView = findViewById(R.id.setting_view);
|
||||
}
|
||||
|
||||
private void initData() {
|
||||
mLivePushSettingView.setDefaultConfig(mAlivcLivePushConfig, LivePushGlobalConfig.ENABLE_BEAUTY, LivePushGlobalConfig.ENABLE_LOCAL_LOG, LivePushGlobalConfig.ENABLE_WATER_MARK);
|
||||
}
|
||||
|
||||
private void initListener() {
|
||||
mLivePushSettingView.bitrateControl.observe(this, isChecked -> {
|
||||
mAlivcLivePushConfig.setEnableBitrateControl(isChecked);
|
||||
});
|
||||
mLivePushSettingView.targetVideoBitrate.observe(this, bitrate -> {
|
||||
SharedPreferenceUtils.setHintTargetBit(getApplicationContext(), bitrate);
|
||||
mAlivcLivePushConfig.setTargetVideoBitrate(bitrate);
|
||||
});
|
||||
mLivePushSettingView.minVideoBitrate.observe(this, bitrate -> {
|
||||
SharedPreferenceUtils.setHintMinBit(getApplicationContext(), bitrate);
|
||||
mAlivcLivePushConfig.setMinVideoBitrate(bitrate);
|
||||
});
|
||||
mLivePushSettingView.initialVideoBitrate.observe(this, bitrate -> {
|
||||
SharedPreferenceUtils.setHintMinBit(getApplicationContext(), bitrate);
|
||||
mAlivcLivePushConfig.setInitialVideoBitrate(bitrate);
|
||||
});
|
||||
mLivePushSettingView.audioBitrate.observe(this, bitrate -> {
|
||||
SharedPreferenceUtils.setHintMinBit(getApplicationContext(), bitrate);
|
||||
mAlivcLivePushConfig.setAudioBitRate(bitrate);
|
||||
});
|
||||
mLivePushSettingView.variableResolution.observe(this, isChecked -> {
|
||||
mAlivcLivePushConfig.setEnableAutoResolution(isChecked);
|
||||
});
|
||||
mLivePushSettingView.minFps.observe(this, minFps -> {
|
||||
mAlivcLivePushConfig.setMinFps(minFps);
|
||||
});
|
||||
mLivePushSettingView.audioSampleRate.observe(this, sampleRate -> {
|
||||
mAlivcLivePushConfig.setAudioSampleRate(sampleRate);
|
||||
});
|
||||
mLivePushSettingView.gop.observe(this, gop -> {
|
||||
mAlivcLivePushConfig.setVideoEncodeGop(gop);
|
||||
});
|
||||
mLivePushSettingView.fps.observe(this, fps -> {
|
||||
mAlivcLivePushConfig.setFps(fps);
|
||||
});
|
||||
mLivePushSettingView.videoHardwareDecode.observe(this, isChecked -> {
|
||||
mAlivcLivePushConfig.setVideoEncodeMode(isChecked ? AlivcEncodeModeEnum.Encode_MODE_HARD : AlivcEncodeModeEnum.Encode_MODE_SOFT);
|
||||
});
|
||||
mLivePushSettingView.audioHardwareDecode.observe(this, isChecked -> {
|
||||
mAlivcLivePushConfig.setAudioEncodeMode(isChecked ? AlivcEncodeModeEnum.Encode_MODE_HARD : AlivcEncodeModeEnum.Encode_MODE_SOFT);
|
||||
});
|
||||
mLivePushSettingView.pushMirror.observe(this, isChecked -> {
|
||||
mAlivcLivePushConfig.setPushMirror(isChecked);
|
||||
});
|
||||
mLivePushSettingView.previewMirror.observe(this, isChecked -> {
|
||||
mAlivcLivePushConfig.setPreviewMirror(isChecked);
|
||||
});
|
||||
mLivePushSettingView.enableFrontCamera.observe(this, isChecked -> {
|
||||
mAlivcLivePushConfig.setCameraType(isChecked ? AlivcLivePushCameraTypeEnum.CAMERA_TYPE_FRONT : AlivcLivePushCameraTypeEnum.CAMERA_TYPE_BACK);
|
||||
// mCameraId = (isChecked ? AlivcLivePushCameraTypeEnum.CAMERA_TYPE_FRONT.getCameraId() : AlivcLivePushCameraTypeEnum.CAMERA_TYPE_BACK.getCameraId());
|
||||
});
|
||||
mLivePushSettingView.autoFocus.observe(this, isChecked -> {
|
||||
mAlivcLivePushConfig.setAutoFocus(isChecked);
|
||||
});
|
||||
mLivePushSettingView.enableBeauty.observe(this, isChecked -> {
|
||||
LivePushGlobalConfig.ENABLE_BEAUTY = isChecked;
|
||||
});
|
||||
mLivePushSettingView.videoOnly.observe(this, isChecked -> {
|
||||
mAlivcLivePushConfig.setVideoOnly(isChecked);
|
||||
});
|
||||
mLivePushSettingView.audioOnly.observe(this, isChecked -> {
|
||||
mAlivcLivePushConfig.setAudioOnly(isChecked);
|
||||
});
|
||||
mLivePushSettingView.pauseImage.observe(this, isChecked -> {
|
||||
if (!isChecked) {
|
||||
mAlivcLivePushConfig.setPausePushImage("");
|
||||
} else {
|
||||
if (mAlivcLivePushConfig.getPreviewOrientation() == AlivcPreviewOrientationEnum.ORIENTATION_LANDSCAPE_HOME_LEFT.getOrientation() || mAlivcLivePushConfig.getPreviewOrientation() == AlivcPreviewOrientationEnum.ORIENTATION_LANDSCAPE_HOME_RIGHT.getOrientation()) {
|
||||
mAlivcLivePushConfig.setPausePushImage(getFilesDir().getPath() + File.separator + "alivc_resource/background_push_land.png");
|
||||
} else {
|
||||
mAlivcLivePushConfig.setPausePushImage(getFilesDir().getPath() + File.separator + "alivc_resource/background_push.png");
|
||||
}
|
||||
}
|
||||
});
|
||||
mLivePushSettingView.netWorkImage.observe(this, isChecked -> {
|
||||
if (!isChecked) {
|
||||
mAlivcLivePushConfig.setNetworkPoorPushImage("");
|
||||
} else {
|
||||
if (mAlivcLivePushConfig.getPreviewOrientation() == AlivcPreviewOrientationEnum.ORIENTATION_LANDSCAPE_HOME_LEFT.getOrientation() || mAlivcLivePushConfig.getPreviewOrientation() == AlivcPreviewOrientationEnum.ORIENTATION_LANDSCAPE_HOME_RIGHT.getOrientation()) {
|
||||
mAlivcLivePushConfig.setNetworkPoorPushImage(getFilesDir().getPath() + File.separator + "alivc_resource/poor_network_land.png");
|
||||
} else {
|
||||
mAlivcLivePushConfig.setNetworkPoorPushImage(getFilesDir().getPath() + File.separator + "alivc_resource/poor_network.png");
|
||||
}
|
||||
}
|
||||
});
|
||||
mLivePushSettingView.musicMode.observe(this, isChecked -> {
|
||||
mAlivcLivePushConfig.setAudioSceneMode(isChecked ? AlivcAudioSceneModeEnum.AUDIO_SCENE_MUSIC_MODE : AlivcAudioSceneModeEnum.AUDIO_SCENE_DEFAULT_MODE);
|
||||
});
|
||||
mLivePushSettingView.extern.observe(this, isChecked -> {
|
||||
mAlivcLivePushConfig.setExternMainStream(isChecked, AlivcImageFormat.IMAGE_FORMAT_YUVNV12, AlivcSoundFormat.SOUND_FORMAT_S16);
|
||||
mAlivcLivePushConfig.setAudioChannels(AlivcAudioChannelEnum.AUDIO_CHANNEL_ONE);
|
||||
mAlivcLivePushConfig.setAudioSampleRate(AlivcAudioSampleRateEnum.AUDIO_SAMPLE_RATE_44100);
|
||||
if (isChecked) {
|
||||
startDownloadYUV();
|
||||
}
|
||||
});
|
||||
mLivePushSettingView.localLog.observe(this, isChecked -> {
|
||||
LivePushGlobalConfig.ENABLE_LOCAL_LOG = isChecked;
|
||||
});
|
||||
|
||||
mLivePushSettingView.previewDisplayMode.observe(this, previewDisplayMode -> {
|
||||
mAlivcLivePushConfig.setPreviewDisplayMode(previewDisplayMode);
|
||||
});
|
||||
|
||||
mLivePushSettingView.cameraCaptureOutputPreference.observe(this, preference -> {
|
||||
mAlivcLivePushConfig.setCameraCaptureOutputPreference(preference);
|
||||
});
|
||||
|
||||
mLivePushSettingView.audioChannel.observe(this, audioChannel -> {
|
||||
mAlivcLivePushConfig.setAudioChannels(audioChannel);
|
||||
});
|
||||
|
||||
mLivePushSettingView.audioProfile.observe(this, audioProfile -> {
|
||||
mAlivcLivePushConfig.setAudioProfile(audioProfile);
|
||||
});
|
||||
|
||||
mLivePushSettingView.videoEncodeType.observe(this, videoEncodeType -> {
|
||||
mAlivcLivePushConfig.setVideoEncodeType(videoEncodeType);
|
||||
});
|
||||
|
||||
mLivePushSettingView.bFrame.observe(this, bFrame -> {
|
||||
mAlivcLivePushConfig.setBFrames(bFrame);
|
||||
});
|
||||
|
||||
mLivePushSettingView.previewOrientation.observe(this, orientation -> {
|
||||
mAlivcLivePushConfig.setPreviewOrientation(orientation);
|
||||
});
|
||||
|
||||
mLivePushSettingView.pauseImagePath.observe(this, path -> {
|
||||
if (mAlivcLivePushConfig.getPausePushImage() == null || mAlivcLivePushConfig.getPausePushImage().equals("")) {
|
||||
mAlivcLivePushConfig.setPausePushImage(path);
|
||||
}
|
||||
});
|
||||
|
||||
mLivePushSettingView.netWorkImagePath.observe(this, path -> {
|
||||
if (mAlivcLivePushConfig.getNetworkPoorPushImage() != null && !mAlivcLivePushConfig.getNetworkPoorPushImage().equals("")) {
|
||||
mAlivcLivePushConfig.setNetworkPoorPushImage(path);
|
||||
}
|
||||
});
|
||||
|
||||
mLivePushSettingView.qualityMode.observe(this, quality -> {
|
||||
mAlivcLivePushConfig.setQualityMode(quality);
|
||||
});
|
||||
|
||||
mLivePushSettingView.resolution.observe(this, resolution -> {
|
||||
this.mCurrentResolution = resolution;
|
||||
if (resolution != AlivcResolutionEnum.RESOLUTION_SELF_DEFINE) {
|
||||
mAlivcLivePushConfig.setResolution(resolution);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
mTabArgsLayout.setOnClickListener(view -> {
|
||||
mTabArgsView.setVisibility(View.VISIBLE);
|
||||
mTabActionView.setVisibility(View.INVISIBLE);
|
||||
mLivePushSettingView.showArgsContent(true);
|
||||
});
|
||||
|
||||
mTabActionLayout.setOnClickListener(view -> {
|
||||
mTabActionView.setVisibility(View.VISIBLE);
|
||||
mTabArgsView.setVisibility(View.INVISIBLE);
|
||||
mLivePushSettingView.showArgsContent(false);
|
||||
});
|
||||
|
||||
mBackImageView.setOnClickListener(view -> {
|
||||
finish();
|
||||
});
|
||||
mCommitButton.setOnClickListener(view -> {
|
||||
if (mCurrentResolution == AlivcResolutionEnum.RESOLUTION_SELF_DEFINE) {
|
||||
AlivcResolutionEnum.RESOLUTION_SELF_DEFINE.setSelfDefineResolution(mLivePushSettingView.getSelfDefineResolutionWidth(), mLivePushSettingView.getSelfDefineResolutionHeight());
|
||||
mAlivcLivePushConfig.setResolution(AlivcResolutionEnum.RESOLUTION_SELF_DEFINE);
|
||||
}
|
||||
mAlivcLivePushConfig.setConnectRetryCount(mLivePushSettingView.getRetryCount());
|
||||
mAlivcLivePushConfig.setConnectRetryInterval(mLivePushSettingView.getRetryInterval());
|
||||
finish();
|
||||
});
|
||||
mMultiInteractSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
LivePushGlobalConfig.IS_MULTI_INTERACT = isChecked;
|
||||
}
|
||||
});
|
||||
mH5CompatibleSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
LivePushGlobalConfig.IS_H5_COMPATIBLE = isChecked;
|
||||
}
|
||||
});
|
||||
mDataChannelSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
LivePushGlobalConfig.IS_DATA_CHANNEL_MESSAGE_ENABLE = isChecked;
|
||||
}
|
||||
});
|
||||
mEarbackOpenWithoutHeadsetSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
LivePushGlobalConfig.IS_EARBACK_OPEN_WITHOUT_HEADSET = isChecked;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void showProgressDialog(@StringRes int tipsResId) {
|
||||
if (null == mLoadingDialog) {
|
||||
mLoadingDialog = new AVLiveLoadingDialog(this).tip(getString(tipsResId));
|
||||
}
|
||||
if (!mLoadingDialog.isShowing()) {
|
||||
mLoadingDialog.show();
|
||||
}
|
||||
}
|
||||
|
||||
private void hideProgressDialog() {
|
||||
if (mLoadingDialog != null && mLoadingDialog.isShowing()) {
|
||||
mLoadingDialog.dismiss();
|
||||
mLoadingDialog = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void startDownloadYUV() {
|
||||
long mCaptureDownloadId = ResourcesDownload.downloadCaptureYUV(this, new DownloadUtil.OnDownloadListener() {
|
||||
@Override
|
||||
public void onDownloadSuccess(long downloadId) {
|
||||
hideProgressDialog();
|
||||
ToastUtils.show("Download Success");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDownloadProgress(long downloadId, double percent) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDownloadError(long downloadId, int errorCode, String errorMsg) {
|
||||
hideProgressDialog();
|
||||
ToastUtils.show(errorMsg);
|
||||
if (errorCode != DownloadManager.ERROR_FILE_ALREADY_EXISTS) {
|
||||
mLivePushSettingView.externDownloadError();
|
||||
}
|
||||
}
|
||||
});
|
||||
showProgressDialog(R.string.waiting_download_video_resources);
|
||||
}
|
||||
|
||||
private String getTextFromCameraCaptureOutputPreference(AlivcLiveCameraCaptureOutputPreference cameraCaptureOutputPreference) {
|
||||
if (cameraCaptureOutputPreference == AlivcLiveCameraCaptureOutputPreference.AUTO) {
|
||||
return getString(R.string.camera_capture_output_preference_auto);
|
||||
} else if (cameraCaptureOutputPreference == AlivcLiveCameraCaptureOutputPreference.PERFORMANCE) {
|
||||
return getString(R.string.camera_capture_output_preference_performance);
|
||||
} else if (cameraCaptureOutputPreference == AlivcLiveCameraCaptureOutputPreference.PREVIEW) {
|
||||
return getString(R.string.camera_capture_output_preference_preview);
|
||||
} else {
|
||||
return getString(R.string.camera_capture_output_preference_preview);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
package com.alivc.live.interactive_common.bean;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import com.alivc.live.annotations.AlivcLiveMode;
|
||||
import com.alivc.live.annotations.AlivcLiveNetworkQuality;
|
||||
import com.alivc.live.commonutils.ToastUtils;
|
||||
import com.alivc.live.interactive_common.listener.InteractLivePushPullListener;
|
||||
import com.alivc.live.player.AlivcLivePlayInfoListener;
|
||||
import com.alivc.live.player.AlivcLivePlayerImpl;
|
||||
import com.alivc.live.player.AlivcLivePlayerStatsInfo;
|
||||
import com.alivc.live.player.annotations.AlivcLivePlayError;
|
||||
|
||||
/**
|
||||
* AlivcLivePlayer 封装类,用于直播连麦拉流
|
||||
*/
|
||||
public class InteractiveLivePlayer extends AlivcLivePlayerImpl {
|
||||
private static final String TAG = InteractiveLivePlayer.class.getSimpleName();
|
||||
|
||||
private InteractLivePushPullListener mListener;
|
||||
private boolean mIsPulling = false;
|
||||
|
||||
private final AlivcLivePlayInfoListener mAlivcLivePlayInfoListener = new AlivcLivePlayInfoListener() {
|
||||
|
||||
@Override
|
||||
public void onPlayStarted() {
|
||||
Log.i(TAG, "onPlayStarted: " + mPullUserData);
|
||||
mIsPulling = true;
|
||||
if (mListener != null) {
|
||||
mListener.onPullSuccess(mPullUserData);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayStopped() {
|
||||
Log.i(TAG, "onPlayStopped: " + mPullUserData);
|
||||
notifyPullStop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFirstVideoFrameDrawn() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNetworkQualityChanged(AlivcLiveNetworkQuality upQuality, AlivcLiveNetworkQuality downQuality) {
|
||||
Log.w(TAG, "onNetworkQualityChanged: " + upQuality + ", " + downQuality);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceiveSEIMessage(int payload, byte[] data) {
|
||||
//Log.d(TAG, "onReceiveSEIMessage: " + payload + ", " + new String(data, StandardCharsets.UTF_8));
|
||||
if (mListener != null) {
|
||||
mListener.onReceiveSEIMessage(payload, data);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceiveDataChannelMessage(byte[] data) {
|
||||
super.onReceiveDataChannelMessage(data);
|
||||
//Log.d(TAG, "onReceiveDataChannelMessage: " + new String(data, StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayoutVolumeUpdate(int volume, boolean isSpeaking) {
|
||||
// 音频音量(仅互动模式下生效,需设置AlivcLivePusher#enableAudioVolumeIndication接口)
|
||||
// Log.d(TAG, "onPlayoutVolumeUpdate: " + volume + ", " + isSpeaking);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAudioMuted(boolean mute) {
|
||||
ToastUtils.show("onAudioMuted: " + mute);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVideoMuted(boolean mute) {
|
||||
ToastUtils.show("onVideoMuted: " + mute);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVideoEnabled(boolean enable) {
|
||||
ToastUtils.show("onVideoEnabled: " + enable);
|
||||
if (mListener != null) {
|
||||
mListener.onVideoEnabled(enable);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVideoResolutionChanged(int width, int height) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(AlivcLivePlayError alivcLivePlayError, String s) {
|
||||
Log.e(TAG, "onError: " + mPullUserData + ", " + alivcLivePlayError + ", " + s);
|
||||
mIsPulling = false;
|
||||
if (mListener != null) {
|
||||
mListener.onPullError(mPullUserData, alivcLivePlayError, s);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayerStatistics(AlivcLivePlayerStatsInfo statsInfo) {
|
||||
//Log.i(TAG, "onPlayerStatistics: " + statsInfo);
|
||||
}
|
||||
};
|
||||
|
||||
private InteractiveUserData mPullUserData;
|
||||
|
||||
public InteractiveLivePlayer(Context context, AlivcLiveMode mode) {
|
||||
super(context, mode);
|
||||
setPlayInfoListener(mAlivcLivePlayInfoListener);
|
||||
}
|
||||
|
||||
public void setMultiInteractPlayInfoListener(InteractLivePushPullListener listener) {
|
||||
this.mListener = listener;
|
||||
}
|
||||
|
||||
public boolean isPulling() {
|
||||
return mIsPulling;
|
||||
}
|
||||
|
||||
public void setPullUserData(InteractiveUserData userData) {
|
||||
this.mPullUserData = userData;
|
||||
}
|
||||
|
||||
// ugly codes: fix the issue of async callbacks not being executed in stopPlay after executing destroy immediately
|
||||
@Override
|
||||
public void destroy() {
|
||||
notifyPullStop();
|
||||
setPlayInfoListener(null);
|
||||
mListener = null;
|
||||
super.destroy();
|
||||
}
|
||||
|
||||
private void notifyPullStop() {
|
||||
mIsPulling = false;
|
||||
if (mListener != null) {
|
||||
mListener.onPullStop(mPullUserData);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.alivc.live.interactive_common.bean;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import com.alivc.live.player.annotations.AlivcLivePlayAudioStreamType;
|
||||
import com.alivc.live.player.annotations.AlivcLivePlayVideoStreamType;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @author keria
|
||||
* @date 2023/10/18
|
||||
* @note 直播连麦推拉流用户信息
|
||||
*/
|
||||
public class InteractiveUserData implements Serializable {
|
||||
|
||||
private static final String TAG = InteractiveUserData.class.getSimpleName();
|
||||
|
||||
public String channelId;
|
||||
public String userId;
|
||||
|
||||
public AlivcLivePlayVideoStreamType videoStreamType;
|
||||
public AlivcLivePlayAudioStreamType audioStreamType;
|
||||
|
||||
public String url;
|
||||
|
||||
public String getKey() {
|
||||
if (!TextUtils.isEmpty(channelId) && !TextUtils.isEmpty(userId)) {
|
||||
return String.format("%s_%s_%s", channelId, userId, videoStreamType == AlivcLivePlayVideoStreamType.STREAM_SCREEN ? "screen" : "camera");
|
||||
} else if (!TextUtils.isEmpty(url)) {
|
||||
return url;
|
||||
}
|
||||
Log.e(TAG, "invalid interactive user data");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getKey();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.alivc.live.interactive_common.listener;
|
||||
|
||||
public interface ConnectionLostListener {
|
||||
void onConfirm();
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.alivc.live.interactive_common.listener;
|
||||
|
||||
import com.alivc.live.interactive_common.bean.InteractiveUserData;
|
||||
import com.alivc.live.player.annotations.AlivcLivePlayError;
|
||||
|
||||
public abstract class InteractLivePushPullListener {
|
||||
public void onPullSuccess(InteractiveUserData userData) {}
|
||||
public void onPullError(InteractiveUserData userData, AlivcLivePlayError errorType, String errorMsg) {}
|
||||
public void onPullStop(InteractiveUserData userData) {}
|
||||
public void onPushSuccess(){}
|
||||
public void onPushError(){}
|
||||
public void onVideoEnabled(boolean enable){}
|
||||
public void onConnectionLost(){}
|
||||
public void onReceiveSEIMessage(int payload, byte[] data) {}
|
||||
public void onPlayerSei(int payload, byte[] uuid, byte[] data) {}
|
||||
public void onReceiveSEIDelay(String src, String type, String msg) {}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.alivc.live.interactive_common.listener;
|
||||
|
||||
import com.alivc.live.interactive_common.bean.InteractiveUserData;
|
||||
|
||||
public abstract class InteractLiveTipsViewListener {
|
||||
public void onCancel() {}
|
||||
public void onConfirm() {}
|
||||
public void onInputConfirm(InteractiveUserData userData) {}
|
||||
public void onQrClick() {}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.alivc.live.interactive_common.manager;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.os.Handler;
|
||||
|
||||
import com.alivc.live.commonutils.BitmapUtil;
|
||||
import com.alivc.live.commonutils.ContextUtils;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @author keria
|
||||
* @date 2023/9/15
|
||||
* @brief 时间戳文字水印管理类
|
||||
* @note 每秒生成一帧文字水印的bitmap,回调给外层进行添加水印处理
|
||||
*/
|
||||
public class TimestampWatermarkManager {
|
||||
|
||||
private static final long TIME_INTERVAL = 1000L;
|
||||
|
||||
private final Handler sHandler = new Handler();
|
||||
|
||||
private final Runnable sRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
processEvent();
|
||||
sHandler.postDelayed(this, TIME_INTERVAL);
|
||||
}
|
||||
};
|
||||
|
||||
private OnWatermarkListener mOnWatermarkListener;
|
||||
|
||||
public void init(OnWatermarkListener listener) {
|
||||
mOnWatermarkListener = listener;
|
||||
startHandler();
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
removeHandler();
|
||||
mOnWatermarkListener = null;
|
||||
}
|
||||
|
||||
public void processEvent() {
|
||||
if (mOnWatermarkListener != null) {
|
||||
String timestamp = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date());
|
||||
Bitmap bitmap = BitmapUtil.createTextBitmap(ContextUtils.getContext(), timestamp);
|
||||
mOnWatermarkListener.onWatermarkUpdate(bitmap);
|
||||
if (bitmap != null && !bitmap.isRecycled()) {
|
||||
bitmap.recycle();
|
||||
bitmap = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void startHandler() {
|
||||
sHandler.postDelayed(sRunnable, TIME_INTERVAL);
|
||||
}
|
||||
|
||||
private void removeHandler() {
|
||||
sHandler.removeCallbacksAndMessages(null);
|
||||
}
|
||||
|
||||
public interface OnWatermarkListener {
|
||||
public void onWatermarkUpdate(Bitmap bitmap);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.alivc.live.interactive_common.utils;
|
||||
|
||||
public enum InteractLiveIntent {
|
||||
/**
|
||||
* 关闭页面
|
||||
*/
|
||||
INTENT_FINISH,
|
||||
/**
|
||||
* 停止拉流
|
||||
*/
|
||||
INTENT_STOP_PULL,
|
||||
/**
|
||||
*
|
||||
* 停止推流
|
||||
*/
|
||||
INTENT_STOP_PUSH
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.alivc.live.interactive_common.utils;
|
||||
|
||||
import com.alivc.live.annotations.AlivcLiveCameraCaptureOutputPreference;
|
||||
import com.alivc.live.pusher.AlivcLivePushConfig;
|
||||
|
||||
/**
|
||||
* 直播连麦公共配置
|
||||
*
|
||||
* @note 用于传递配置页的一些公共参数到直播连麦页面
|
||||
*/
|
||||
public class LivePushGlobalConfig {
|
||||
|
||||
/**
|
||||
* 推流配置
|
||||
*/
|
||||
public static AlivcLivePushConfig mAlivcLivePushConfig = new AlivcLivePushConfig();
|
||||
|
||||
/**
|
||||
* 多人互动模式/PK
|
||||
*/
|
||||
public static boolean IS_MULTI_INTERACT = false;
|
||||
|
||||
/**
|
||||
* 开启 Data Channel 自定义消息通道
|
||||
*/
|
||||
public static boolean IS_DATA_CHANNEL_MESSAGE_ENABLE = false;
|
||||
|
||||
/**
|
||||
* 开启强制耳返
|
||||
*/
|
||||
public static boolean IS_EARBACK_OPEN_WITHOUT_HEADSET = false;
|
||||
|
||||
/**
|
||||
* H5兼容模式(可与web连麦互通)
|
||||
*/
|
||||
public static boolean IS_H5_COMPATIBLE = false;
|
||||
|
||||
/**
|
||||
* 美颜
|
||||
*/
|
||||
public static boolean ENABLE_BEAUTY = true;
|
||||
/**
|
||||
* 本地日志
|
||||
*/
|
||||
public static boolean ENABLE_LOCAL_LOG = true;
|
||||
|
||||
/**
|
||||
* 水印
|
||||
*/
|
||||
public static boolean ENABLE_WATER_MARK = false;
|
||||
|
||||
/**
|
||||
* 摄像头采集偏好
|
||||
*/
|
||||
public static AlivcLiveCameraCaptureOutputPreference CAMERA_CAPTURE_OUTPUT_PREFERENCE = AlivcLiveCameraCaptureOutputPreference.PREVIEW;
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package com.alivc.live.interactive_common.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
|
||||
import com.alivc.live.commonui.avdialog.AUILiveDialog;
|
||||
import com.alivc.live.interactive_common.R;
|
||||
import com.alivc.live.interactive_common.listener.ConnectionLostListener;
|
||||
|
||||
public class ConnectionLostTipsView extends ConstraintLayout {
|
||||
|
||||
private final Context mContext;
|
||||
private final View inflate;
|
||||
private TextView mConfirmTextView;
|
||||
private AUILiveDialog mAUILiveDialog;
|
||||
private ConnectionLostListener mConnectionLostListener;
|
||||
|
||||
public ConnectionLostTipsView(@NonNull Context context) {
|
||||
this(context,null);
|
||||
}
|
||||
|
||||
public ConnectionLostTipsView(@NonNull Context context, AttributeSet attrs) {
|
||||
this(context, attrs,-1);
|
||||
}
|
||||
|
||||
public ConnectionLostTipsView(@NonNull Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
this.mContext = context;
|
||||
mAUILiveDialog = new AUILiveDialog(context);
|
||||
inflate = LayoutInflater.from(mContext).inflate(R.layout.layout_connect_lost_tips_view, this, true);
|
||||
initView();
|
||||
initListener();
|
||||
|
||||
mAUILiveDialog.setContentView(this);
|
||||
}
|
||||
|
||||
private void initView(){
|
||||
mConfirmTextView = inflate.findViewById(R.id.tv_confirm);
|
||||
}
|
||||
|
||||
private void initListener(){
|
||||
mConfirmTextView.setOnClickListener(view -> {
|
||||
if(mConnectionLostListener != null){
|
||||
mConnectionLostListener.onConfirm();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void show(){
|
||||
mAUILiveDialog.show();
|
||||
}
|
||||
|
||||
public void hide(){
|
||||
mAUILiveDialog.dismiss();
|
||||
}
|
||||
|
||||
public void setConnectionLostListener(ConnectionLostListener listener){
|
||||
this.mConnectionLostListener = listener;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.alivc.live.interactive_common.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
|
||||
import com.alivc.live.interactive_common.R;
|
||||
|
||||
public class InteractLiveRadioButton extends ConstraintLayout {
|
||||
|
||||
private final Context mContext;
|
||||
private ConstraintLayout mConstraintLayoutRootView;
|
||||
private TextView mTitleTextView;
|
||||
|
||||
public InteractLiveRadioButton(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public InteractLiveRadioButton(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, -1);
|
||||
}
|
||||
|
||||
public InteractLiveRadioButton(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
this.mContext = context;
|
||||
initView();
|
||||
initAttribute(attrs);
|
||||
}
|
||||
|
||||
private void initView() {
|
||||
View mRootView = LayoutInflater.from(mContext).inflate(R.layout.layout_interact_live_radio_button, this, true);
|
||||
|
||||
mConstraintLayoutRootView = mRootView.findViewById(R.id.root);
|
||||
mTitleTextView = mRootView.findViewById(R.id.tv_title);
|
||||
}
|
||||
|
||||
private void initAttribute(AttributeSet attrs) {
|
||||
TypedArray typedArray = mContext.obtainStyledAttributes(attrs, R.styleable.InteractLiveRadioButton);
|
||||
String mText = typedArray.getString(R.styleable.InteractLiveRadioButton_ilrb_text);
|
||||
typedArray.recycle();
|
||||
|
||||
mTitleTextView.setText(mText);
|
||||
}
|
||||
|
||||
public void setChecked(boolean isChecked) {
|
||||
if (isChecked) {
|
||||
mConstraintLayoutRootView.setBackground(getResources().getDrawable(R.drawable.shape_interact_live_selected));
|
||||
} else {
|
||||
mConstraintLayoutRootView.setBackground(getResources().getDrawable(R.drawable.shape_interact_live_unselected));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
package com.alivc.live.interactive_common.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.RadioButton;
|
||||
import android.widget.RadioGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
|
||||
import com.alivc.live.interactive_common.R;
|
||||
import com.alivc.live.interactive_common.bean.InteractiveUserData;
|
||||
import com.alivc.live.interactive_common.listener.InteractLiveTipsViewListener;
|
||||
import com.alivc.live.player.annotations.AlivcLivePlayAudioStreamType;
|
||||
import com.alivc.live.player.annotations.AlivcLivePlayVideoStreamType;
|
||||
|
||||
/**
|
||||
* 直播连麦用户信息输入框
|
||||
*/
|
||||
public class InteractiveCommonInputView extends ConstraintLayout {
|
||||
|
||||
public enum ViewType {
|
||||
INTERACTIVE,
|
||||
PK,
|
||||
BARE_STREAM
|
||||
}
|
||||
|
||||
private ViewType mViewType;
|
||||
|
||||
private final Context mContext;
|
||||
private final View inflate;
|
||||
|
||||
private TextView mCancelTextView;
|
||||
private TextView mConfirmTextView;
|
||||
|
||||
private TextView mTipsTextView;
|
||||
private EditText mInputUserIdEditText;
|
||||
private EditText mInputRoomIdEditText;
|
||||
private EditText mInputUrlEditText;
|
||||
|
||||
private ImageView mClearUserIdImageView;
|
||||
private ImageView mClearRoomIdImageView;
|
||||
private ImageView mScanQrImageView;
|
||||
|
||||
private LinearLayout mVideoRadioGroupLayout;
|
||||
private RadioGroup mVideoStreamTypeRadioGroup;
|
||||
private RadioButton mVideoCamaraRadioButton;
|
||||
private RadioButton mVideoScreenRadioButton;
|
||||
|
||||
private LinearLayout mAudioRadioGroupLayout;
|
||||
private RadioGroup mAudioStreamTypeRadioGroup;
|
||||
private RadioButton mAudioMicRadioButton;
|
||||
private RadioButton mAudioDualRadioButton;
|
||||
|
||||
private InteractLiveTipsViewListener mInteractLiveTipsViewListener;
|
||||
|
||||
private boolean mShowInputView;
|
||||
|
||||
public InteractiveCommonInputView(@NonNull Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public InteractiveCommonInputView(@NonNull Context context, AttributeSet attrs) {
|
||||
this(context, attrs, -1);
|
||||
}
|
||||
|
||||
public InteractiveCommonInputView(@NonNull Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
this.mContext = context;
|
||||
inflate = LayoutInflater.from(mContext).inflate(R.layout.layout_interactive_common_input, this, true);
|
||||
initView();
|
||||
initListener();
|
||||
}
|
||||
|
||||
private void initView() {
|
||||
mCancelTextView = inflate.findViewById(R.id.tv_cancel);
|
||||
mConfirmTextView = inflate.findViewById(R.id.tv_confirm);
|
||||
mTipsTextView = inflate.findViewById(R.id.tv_tips);
|
||||
|
||||
mInputUserIdEditText = inflate.findViewById(R.id.et_input_user_id);
|
||||
mInputRoomIdEditText = inflate.findViewById(R.id.et_input_room_id);
|
||||
mInputUrlEditText = inflate.findViewById(R.id.et_input_url);
|
||||
|
||||
mClearUserIdImageView = inflate.findViewById(R.id.iv_clear_user_id);
|
||||
mClearRoomIdImageView = inflate.findViewById(R.id.iv_clear_room_id);
|
||||
mScanQrImageView = inflate.findViewById(R.id.iv_scan_qr);
|
||||
|
||||
mVideoRadioGroupLayout = inflate.findViewById(R.id.video_radio_group);
|
||||
mVideoStreamTypeRadioGroup = inflate.findViewById(R.id.rg_video_stream_type);
|
||||
mVideoCamaraRadioButton = inflate.findViewById(R.id.rb_camera);
|
||||
mVideoScreenRadioButton = inflate.findViewById(R.id.rb_screen);
|
||||
|
||||
mAudioRadioGroupLayout = inflate.findViewById(R.id.audio_radio_group);
|
||||
mAudioStreamTypeRadioGroup = inflate.findViewById(R.id.rg_audio_stream_type);
|
||||
mAudioMicRadioButton = inflate.findViewById(R.id.rb_mic);
|
||||
mAudioDualRadioButton = inflate.findViewById(R.id.rb_dual);
|
||||
}
|
||||
|
||||
private void initListener() {
|
||||
mCancelTextView.setOnClickListener(view -> {
|
||||
if (mInteractLiveTipsViewListener != null) {
|
||||
mInteractLiveTipsViewListener.onCancel();
|
||||
}
|
||||
});
|
||||
|
||||
mConfirmTextView.setOnClickListener(view -> {
|
||||
if (mInteractLiveTipsViewListener == null) {
|
||||
return;
|
||||
}
|
||||
if (!mShowInputView) {
|
||||
mInteractLiveTipsViewListener.onConfirm();
|
||||
return;
|
||||
}
|
||||
InteractiveUserData inputData = new InteractiveUserData();
|
||||
if (mViewType == ViewType.INTERACTIVE) {
|
||||
inputData.userId = mInputUserIdEditText.getText().toString().trim();
|
||||
} else if (mViewType == ViewType.PK) {
|
||||
inputData.userId = mInputUserIdEditText.getText().toString().trim();
|
||||
inputData.channelId = mInputRoomIdEditText.getText().toString().trim();
|
||||
} else if (mViewType == ViewType.BARE_STREAM) {
|
||||
inputData.url = mInputUrlEditText.getText().toString().trim();
|
||||
}
|
||||
inputData.videoStreamType = (mVideoStreamTypeRadioGroup.getCheckedRadioButtonId() == mVideoCamaraRadioButton.getId())
|
||||
? AlivcLivePlayVideoStreamType.STREAM_CAMERA
|
||||
: AlivcLivePlayVideoStreamType.STREAM_SCREEN;
|
||||
inputData.audioStreamType = (mAudioStreamTypeRadioGroup.getCheckedRadioButtonId() == mAudioMicRadioButton.getId())
|
||||
? AlivcLivePlayAudioStreamType.STREAM_MIC
|
||||
: AlivcLivePlayAudioStreamType.STREAM_DUAL;
|
||||
mInteractLiveTipsViewListener.onInputConfirm(inputData);
|
||||
});
|
||||
|
||||
mClearUserIdImageView.setOnClickListener(view -> mInputUserIdEditText.setText(""));
|
||||
mClearRoomIdImageView.setOnClickListener(view -> mInputRoomIdEditText.setText(""));
|
||||
mScanQrImageView.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (mInteractLiveTipsViewListener != null) {
|
||||
mInteractLiveTipsViewListener.onQrClick();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setViewType(ViewType viewType) {
|
||||
mViewType = viewType;
|
||||
|
||||
if (viewType == ViewType.INTERACTIVE) {
|
||||
mInputUserIdEditText.setVisibility(View.VISIBLE);
|
||||
mClearUserIdImageView.setVisibility(View.VISIBLE);
|
||||
mInputRoomIdEditText.setVisibility(View.GONE);
|
||||
mClearRoomIdImageView.setVisibility(View.GONE);
|
||||
mInputUrlEditText.setVisibility(View.GONE);
|
||||
mScanQrImageView.setVisibility(View.GONE);
|
||||
mTipsTextView.setVisibility(View.VISIBLE);
|
||||
mAudioRadioGroupLayout.setVisibility(View.VISIBLE);
|
||||
mVideoRadioGroupLayout.setVisibility(View.VISIBLE);
|
||||
} else if (viewType == ViewType.PK) {
|
||||
mInputUserIdEditText.setVisibility(View.VISIBLE);
|
||||
mClearUserIdImageView.setVisibility(View.VISIBLE);
|
||||
mInputRoomIdEditText.setVisibility(View.VISIBLE);
|
||||
mClearRoomIdImageView.setVisibility(View.VISIBLE);
|
||||
mInputUrlEditText.setVisibility(View.GONE);
|
||||
mScanQrImageView.setVisibility(View.GONE);
|
||||
mTipsTextView.setVisibility(View.VISIBLE);
|
||||
mAudioRadioGroupLayout.setVisibility(View.VISIBLE);
|
||||
mVideoRadioGroupLayout.setVisibility(View.VISIBLE);
|
||||
} else if (viewType == ViewType.BARE_STREAM) {
|
||||
mInputUserIdEditText.setVisibility(View.GONE);
|
||||
mInputRoomIdEditText.setVisibility(View.GONE);
|
||||
mClearUserIdImageView.setVisibility(View.GONE);
|
||||
mClearRoomIdImageView.setVisibility(View.GONE);
|
||||
mInputUrlEditText.setVisibility(View.VISIBLE);
|
||||
mScanQrImageView.setVisibility(View.VISIBLE);
|
||||
mTipsTextView.setVisibility(View.VISIBLE);
|
||||
mAudioRadioGroupLayout.setVisibility(View.GONE);
|
||||
mVideoRadioGroupLayout.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
public void setDefaultRoomId(String roomId) {
|
||||
if (!TextUtils.isEmpty(roomId)) {
|
||||
mInputRoomIdEditText.setText(roomId);
|
||||
}
|
||||
}
|
||||
|
||||
public void showInputView(String content, boolean showInputView) {
|
||||
this.mShowInputView = showInputView;
|
||||
if (!mShowInputView) {
|
||||
mInputUserIdEditText.setVisibility(View.GONE);
|
||||
mInputRoomIdEditText.setVisibility(View.GONE);
|
||||
mInputUrlEditText.setVisibility(View.GONE);
|
||||
mClearUserIdImageView.setVisibility(View.GONE);
|
||||
mClearRoomIdImageView.setVisibility(View.GONE);
|
||||
mTipsTextView.setVisibility(View.VISIBLE);
|
||||
mTipsTextView.setText(content);
|
||||
mAudioRadioGroupLayout.setVisibility(View.GONE);
|
||||
mVideoRadioGroupLayout.setVisibility(View.GONE);
|
||||
} else {
|
||||
setViewType(mViewType);
|
||||
}
|
||||
}
|
||||
|
||||
public void setQrResult(String content) {
|
||||
mInputUrlEditText.setText(content);
|
||||
}
|
||||
|
||||
public void setOnInteractLiveTipsViewListener(InteractLiveTipsViewListener listener) {
|
||||
this.mInteractLiveTipsViewListener = listener;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
package com.alivc.live.interactive_common.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.alivc.live.interactive_common.R;
|
||||
|
||||
/**
|
||||
* 连麦 View
|
||||
*/
|
||||
public class InteractiveConnectView extends LinearLayout {
|
||||
|
||||
private FrameLayout mConnectFrameLayout;
|
||||
private TextView mConnectTextView;
|
||||
private OnConnectClickListener mOnConnectClickListener;
|
||||
|
||||
public InteractiveConnectView(@NonNull Context context) {
|
||||
super(context);
|
||||
init(context);
|
||||
}
|
||||
|
||||
public InteractiveConnectView(@NonNull Context context, @Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init(context);
|
||||
}
|
||||
|
||||
public InteractiveConnectView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
init(context);
|
||||
}
|
||||
|
||||
private void init(Context context) {
|
||||
setOrientation(LinearLayout.VERTICAL);
|
||||
View inflate = LayoutInflater.from(context).inflate(R.layout.layout_interactive_connect_view, this, true);
|
||||
mConnectFrameLayout = inflate.findViewById(R.id.fl_un_connect);
|
||||
mConnectTextView = inflate.findViewById(R.id.tv_connect);
|
||||
|
||||
initListener();
|
||||
}
|
||||
|
||||
private void initListener() {
|
||||
mConnectTextView.setOnClickListener(view -> {
|
||||
if (mOnConnectClickListener != null) {
|
||||
mOnConnectClickListener.onConnectClick();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void isShow(boolean isShowSurfaceView) {
|
||||
mConnectFrameLayout.setVisibility(isShowSurfaceView ? View.INVISIBLE : View.VISIBLE);
|
||||
}
|
||||
|
||||
public void setDefaultText(String text) {
|
||||
mConnectTextView.setText(text);
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始连接
|
||||
*/
|
||||
public void connected() {
|
||||
connected(getResources().getString(R.string.interact_stop_connect));
|
||||
}
|
||||
|
||||
/**
|
||||
* 断开连接
|
||||
*/
|
||||
public void unConnected() {
|
||||
unConnected(getResources().getString(R.string.interact_start_connect));
|
||||
}
|
||||
|
||||
public void connected(String text) {
|
||||
mConnectTextView.setText(text);
|
||||
mConnectTextView.setBackground(getResources().getDrawable(R.drawable.shape_interact_live_un_connect_btn_bg));
|
||||
}
|
||||
|
||||
public void unConnected(String text) {
|
||||
mConnectTextView.setText(text);
|
||||
mConnectTextView.setBackground(getResources().getDrawable(R.drawable.shape_pysh_btn_bg));
|
||||
}
|
||||
|
||||
public FrameLayout getConnectFrameLayout() {
|
||||
return mConnectFrameLayout;
|
||||
}
|
||||
|
||||
public TextView getConnectTextView() {
|
||||
return mConnectTextView;
|
||||
}
|
||||
|
||||
|
||||
public void setConnectClickListener(OnConnectClickListener listener) {
|
||||
this.mOnConnectClickListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* 连麦按钮点击监听
|
||||
*/
|
||||
public interface OnConnectClickListener {
|
||||
void onConnectClick();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
package com.alivc.live.interactive_common.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.alivc.live.interactive_common.R;
|
||||
|
||||
/**
|
||||
* @author keria
|
||||
* @date 2024/5/28
|
||||
* @brief 直播连麦窗格控制操作栏
|
||||
*/
|
||||
public class InteractivePaneControlView extends LinearLayout {
|
||||
|
||||
private ImageView mInteractMuteImageView;
|
||||
private ImageView mInteractSetEmptyView;
|
||||
private ImageView mInteractResizeView;
|
||||
|
||||
private OnClickEventListener mOnClickEventListener;
|
||||
|
||||
private boolean mMuteAudio = false;
|
||||
private boolean mEmptyView = false;
|
||||
private boolean mResize = false;
|
||||
|
||||
public InteractivePaneControlView(Context context) {
|
||||
super(context);
|
||||
init(context);
|
||||
}
|
||||
|
||||
public InteractivePaneControlView(Context context, @Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init(context);
|
||||
}
|
||||
|
||||
public InteractivePaneControlView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
init(context);
|
||||
}
|
||||
|
||||
private void init(Context context) {
|
||||
LayoutInflater.from(context).inflate(R.layout.layout_interactive_pane_control, this, true);
|
||||
|
||||
mInteractMuteImageView = findViewById(R.id.iv_mute_audio);
|
||||
mInteractSetEmptyView = findViewById(R.id.iv_show_view);
|
||||
mInteractResizeView = findViewById(R.id.iv_view_resize);
|
||||
|
||||
setupListeners();
|
||||
}
|
||||
|
||||
private void setupListeners() {
|
||||
mInteractMuteImageView.setOnClickListener(v -> toggleState(
|
||||
() -> mMuteAudio = !mMuteAudio,
|
||||
() -> mInteractMuteImageView.setImageResource(mMuteAudio ? R.drawable.ic_interact_mute : R.drawable.ic_interact_unmute),
|
||||
mOnClickEventListener != null ? () -> mOnClickEventListener.onClickMuteAudio(mMuteAudio) : null
|
||||
));
|
||||
|
||||
mInteractSetEmptyView.setOnClickListener(v -> toggleState(
|
||||
() -> mEmptyView = !mEmptyView,
|
||||
() -> mInteractSetEmptyView.setImageResource(mEmptyView ? R.drawable.ic_view_container_hide : R.drawable.ic_view_container_show),
|
||||
mOnClickEventListener != null ? () -> mOnClickEventListener.onClickEmptyView(mEmptyView) : null
|
||||
));
|
||||
|
||||
mInteractResizeView.setOnClickListener(v -> toggleState(
|
||||
() -> mResize = !mResize,
|
||||
() -> mInteractResizeView.setImageResource(mResize ? R.drawable.ic_full_screen : R.drawable.ic_half_screen),
|
||||
mOnClickEventListener != null ? () -> mOnClickEventListener.onClickResize(mResize) : null
|
||||
));
|
||||
}
|
||||
|
||||
private void toggleState(@Nullable Runnable viewState, @Nullable Runnable viewStateToggler, @Nullable Runnable eventCallback) {
|
||||
// 执行状态转换
|
||||
if (viewState != null) {
|
||||
viewState.run();
|
||||
}
|
||||
|
||||
// 更新视图状态
|
||||
if (viewStateToggler != null) {
|
||||
viewStateToggler.run();
|
||||
}
|
||||
|
||||
// 执行事件回调
|
||||
if (eventCallback != null) {
|
||||
eventCallback.run();
|
||||
}
|
||||
}
|
||||
|
||||
public void enableMute(boolean mute) {
|
||||
if (mInteractMuteImageView != null) {
|
||||
mInteractMuteImageView.setVisibility(mute ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
public void setOnClickEventListener(OnClickEventListener listener) {
|
||||
this.mOnClickEventListener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow();
|
||||
mOnClickEventListener = null;
|
||||
}
|
||||
|
||||
public interface OnClickEventListener {
|
||||
void onClickMuteAudio(boolean mute);
|
||||
|
||||
void onClickEmptyView(boolean empty);
|
||||
|
||||
void onClickResize(boolean resize);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package com.alivc.live.interactive_common.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
|
||||
import com.alivc.live.interactive_common.R;
|
||||
|
||||
public class InteractiveQrEditView extends ConstraintLayout {
|
||||
|
||||
private TextView titleView;
|
||||
private EditText etContentView;
|
||||
private ImageView qrImageView;
|
||||
private OnQrClickListener mQrClickListener;
|
||||
|
||||
public InteractiveQrEditView(@NonNull Context context) {
|
||||
super(context);
|
||||
init(context, null);
|
||||
}
|
||||
|
||||
public InteractiveQrEditView(@NonNull Context context, @Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init(context, attrs);
|
||||
}
|
||||
|
||||
public InteractiveQrEditView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
init(context, attrs);
|
||||
}
|
||||
|
||||
private void init(Context context, AttributeSet attrs) {
|
||||
View inflateView = LayoutInflater.from(context).inflate(R.layout.layout_interactive_qr_input_view, this, true);
|
||||
titleView = inflateView.findViewById(R.id.tv_interactive_pull_url);
|
||||
etContentView = inflateView.findViewById(R.id.et_interactive_pull_url);
|
||||
qrImageView = inflateView.findViewById(R.id.iv_qr_pull_url);
|
||||
|
||||
if (attrs != null) {
|
||||
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.InteractiveQrEditView);
|
||||
String title = typedArray.getString(R.styleable.InteractiveQrEditView_title);
|
||||
typedArray.recycle();
|
||||
titleView.setText(title);
|
||||
}
|
||||
|
||||
initListener();
|
||||
}
|
||||
|
||||
private void initListener() {
|
||||
qrImageView.setOnClickListener(view -> {
|
||||
if (mQrClickListener != null) {
|
||||
mQrClickListener.onQrClick();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
titleView.setText(title);
|
||||
}
|
||||
|
||||
public void setEditText(String text) {
|
||||
etContentView.setText(text);
|
||||
}
|
||||
|
||||
public String getEditText() {
|
||||
return etContentView.getText().toString();
|
||||
}
|
||||
|
||||
public void setOnQrClickListener(OnQrClickListener listener) {
|
||||
this.mQrClickListener = listener;
|
||||
}
|
||||
|
||||
public interface OnQrClickListener {
|
||||
void onQrClick();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
package com.alivc.live.interactive_common.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.alivc.live.interactive_common.R;
|
||||
|
||||
/**
|
||||
* @Author keria
|
||||
* @Date 2024/5/28
|
||||
* @Brief 直播连麦房间控制操作栏
|
||||
*/
|
||||
public class InteractiveRoomControlView extends LinearLayout {
|
||||
private ImageView mSwitchCameraIv;
|
||||
private ImageView mUseSpeakerPhoneIv;
|
||||
private ImageView mMuteAudioIv;
|
||||
private ImageView mMuteVideoIv;
|
||||
private ImageView mEnableAudioCaptureIv;
|
||||
private ImageView mEnableVideoCaptureIv;
|
||||
|
||||
private boolean mUseSpeakerPhone = false;
|
||||
private boolean mMuteAudio = false;
|
||||
private boolean mMuteVideo = false;
|
||||
private boolean mEnableAudioCapture = true;
|
||||
private boolean mEnableVideoCapture = true;
|
||||
|
||||
private OnClickEventListener mOnClickEventListener;
|
||||
|
||||
public InteractiveRoomControlView(Context context) {
|
||||
super(context);
|
||||
init(context);
|
||||
}
|
||||
|
||||
public InteractiveRoomControlView(Context context, @Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init(context);
|
||||
}
|
||||
|
||||
public InteractiveRoomControlView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
init(context);
|
||||
}
|
||||
|
||||
private void init(Context context) {
|
||||
LayoutInflater.from(context).inflate(R.layout.layout_interactive_room_control, this, true);
|
||||
|
||||
mSwitchCameraIv = findViewById(R.id.iv_camera);
|
||||
mUseSpeakerPhoneIv = findViewById(R.id.iv_speaker_phone);
|
||||
mMuteAudioIv = findViewById(R.id.iv_mute_audio);
|
||||
mMuteVideoIv = findViewById(R.id.iv_mute_video);
|
||||
mEnableAudioCaptureIv = findViewById(R.id.iv_enable_audio);
|
||||
mEnableVideoCaptureIv = findViewById(R.id.iv_enable_video);
|
||||
|
||||
setupListeners();
|
||||
}
|
||||
|
||||
private void setupListeners() {
|
||||
mSwitchCameraIv.setOnClickListener(v -> toggleState(
|
||||
null,
|
||||
null,
|
||||
mOnClickEventListener != null ? () -> mOnClickEventListener.onClickSwitchCamera() : null
|
||||
));
|
||||
|
||||
mUseSpeakerPhoneIv.setOnClickListener(v -> toggleState(
|
||||
() -> mUseSpeakerPhone = !mUseSpeakerPhone,
|
||||
() -> mUseSpeakerPhoneIv.setImageResource(mUseSpeakerPhone ? R.drawable.ic_speaker_phone_on : R.drawable.ic_speaker_phone_off),
|
||||
mOnClickEventListener != null ? () -> mOnClickEventListener.onClickSpeakerPhone(mUseSpeakerPhone) : null
|
||||
));
|
||||
|
||||
mMuteAudioIv.setOnClickListener(v -> toggleState(
|
||||
() -> mMuteAudio = !mMuteAudio,
|
||||
() -> mMuteAudioIv.setImageResource(mMuteAudio ? R.drawable.ic_audio_capture_off : R.drawable.ic_audio_capture_on),
|
||||
mOnClickEventListener != null ? () -> mOnClickEventListener.onClickMuteAudio(mMuteAudio) : null
|
||||
));
|
||||
|
||||
mMuteVideoIv.setOnClickListener(v -> toggleState(
|
||||
() -> mMuteVideo = !mMuteVideo,
|
||||
() -> mMuteVideoIv.setImageResource(mMuteVideo ? R.drawable.ic_local_video_off : R.drawable.ic_local_video_on),
|
||||
mOnClickEventListener != null ? () -> mOnClickEventListener.onClickMuteVideo(mMuteVideo) : null
|
||||
));
|
||||
|
||||
mEnableAudioCaptureIv.setOnClickListener(v -> toggleState(
|
||||
() -> mEnableAudioCapture = !mEnableAudioCapture,
|
||||
() -> mEnableAudioCaptureIv.setImageResource(mEnableAudioCapture ? R.drawable.ic_audio_capture_on : R.drawable.ic_audio_capture_off),
|
||||
mOnClickEventListener != null ? () -> mOnClickEventListener.onClickEnableAudio(mEnableAudioCapture) : null
|
||||
));
|
||||
|
||||
mEnableVideoCaptureIv.setOnClickListener(v -> toggleState(
|
||||
() -> mEnableVideoCapture = !mEnableVideoCapture,
|
||||
() -> mEnableVideoCaptureIv.setImageResource(mEnableVideoCapture ? R.drawable.ic_local_video_on : R.drawable.ic_local_video_off),
|
||||
mOnClickEventListener != null ? () -> mOnClickEventListener.onClickEnableVideo(mEnableVideoCapture) : null
|
||||
));
|
||||
}
|
||||
|
||||
private void toggleState(@Nullable Runnable viewState, @Nullable Runnable viewStateToggler, @Nullable Runnable eventCallback) {
|
||||
// 执行状态转换
|
||||
if (viewState != null) {
|
||||
viewState.run();
|
||||
}
|
||||
|
||||
// 更新视图状态
|
||||
if (viewStateToggler != null) {
|
||||
viewStateToggler.run();
|
||||
}
|
||||
|
||||
// 执行事件回调
|
||||
if (eventCallback != null) {
|
||||
eventCallback.run();
|
||||
}
|
||||
}
|
||||
|
||||
public void setOnClickEventListener(OnClickEventListener listener) {
|
||||
this.mOnClickEventListener = listener;
|
||||
}
|
||||
|
||||
public interface OnClickEventListener {
|
||||
void onClickSwitchCamera();
|
||||
|
||||
void onClickSpeakerPhone(boolean enable);
|
||||
|
||||
void onClickMuteAudio(boolean mute);
|
||||
|
||||
void onClickMuteVideo(boolean mute);
|
||||
|
||||
void onClickEnableAudio(boolean enable);
|
||||
|
||||
void onClickEnableVideo(boolean enable);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.alivc.live.interactive_common.widget;
|
||||
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
public class MultiAlivcLiveView {
|
||||
|
||||
private FrameLayout mUnConnectFrameLayout;
|
||||
private FrameLayout mSmallFrameLayout;
|
||||
private TextView mConnectTextView;
|
||||
|
||||
public MultiAlivcLiveView(FrameLayout unConnectFrameLayout,FrameLayout smallFrameLayout,TextView connectTextView){
|
||||
this.mUnConnectFrameLayout = unConnectFrameLayout;
|
||||
this.mSmallFrameLayout = smallFrameLayout;
|
||||
this.mConnectTextView = connectTextView;
|
||||
}
|
||||
|
||||
public FrameLayout getUnConnectFrameLayout() {
|
||||
return mUnConnectFrameLayout;
|
||||
}
|
||||
|
||||
public void setUnConnectFrameLayout(FrameLayout mUnConnectFrameLayout) {
|
||||
this.mUnConnectFrameLayout = mUnConnectFrameLayout;
|
||||
}
|
||||
|
||||
public FrameLayout getSmallFrameLayout() {
|
||||
return mSmallFrameLayout;
|
||||
}
|
||||
|
||||
public void setSmallFrameLayout(FrameLayout mSmallFrameLayout) {
|
||||
this.mSmallFrameLayout = mSmallFrameLayout;
|
||||
}
|
||||
|
||||
public TextView getConnectTextView() {
|
||||
return mConnectTextView;
|
||||
}
|
||||
|
||||
public void setConnectTextView(TextView mConnectTextView) {
|
||||
this.mConnectTextView = mConnectTextView;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package com.alivc.live.interactive_common.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.alivc.live.commonbiz.test.AliLiveStreamURLUtil;
|
||||
import com.alivc.live.interactive_common.R;
|
||||
import com.alivc.live.interactive_common.bean.InteractiveUserData;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* 用户连麦窗口显示'房间ID'和'用户ID'
|
||||
*/
|
||||
public class RoomAndUserInfoView extends LinearLayout {
|
||||
private TextView mInfoTv;
|
||||
|
||||
public RoomAndUserInfoView(@NonNull Context context) {
|
||||
super(context);
|
||||
init(context);
|
||||
}
|
||||
|
||||
public RoomAndUserInfoView(@NonNull Context context, @Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init(context);
|
||||
}
|
||||
|
||||
public RoomAndUserInfoView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
init(context);
|
||||
}
|
||||
|
||||
private void init(Context context) {
|
||||
View inflateView = LayoutInflater.from(context).inflate(R.layout.layout_room_user_info_view, this, true);
|
||||
mInfoTv = inflateView.findViewById(R.id.tv_info);
|
||||
}
|
||||
|
||||
public void setUserInfo(String channelId, String userId) {
|
||||
mInfoTv.setText(String.format("Room:%s\nUser:%s", channelId, userId));
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
HashMap<String, String> params = AliLiveStreamURLUtil.parseUrl(url);
|
||||
mInfoTv.setText(String.format("Room:%s\nUser:%s",
|
||||
AliLiveStreamURLUtil.parseURLStreamName(params),
|
||||
params.get(AliLiveStreamURLUtil.USER_ID)));
|
||||
}
|
||||
|
||||
public void setUserData(InteractiveUserData userData) {
|
||||
mInfoTv.setText(String.format("Room:%s\nUser:%s",
|
||||
userData != null ? userData.channelId : "",
|
||||
userData != null ? userData.userId : ""));
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 6.6 KiB |
|
After Width: | Height: | Size: 5.5 KiB |
|
After Width: | Height: | Size: 187 KiB |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 502 B |
|
After Width: | Height: | Size: 822 B |
|
After Width: | Height: | Size: 484 B |
|
After Width: | Height: | Size: 477 B |
|
After Width: | Height: | Size: 568 B |
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 6.3 KiB |
|
After Width: | Height: | Size: 4.5 KiB |
|
After Width: | Height: | Size: 6.1 KiB |
|
After Width: | Height: | Size: 4.6 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 257 B |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 6.0 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
|
After Width: | Height: | Size: 5.5 KiB |
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<corners android:radius="16dp" />
|
||||
<solid android:color="#23262F" />
|
||||
</shape>
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<stroke android:width="1dp" android:color="#747A8C"/>
|
||||
<corners android:radius="2dp" />
|
||||
</shape>
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
|
||||
<solid android:color="#23262F" />
|
||||
|
||||
<corners android:radius="12dp" />
|
||||
|
||||
<stroke android:color="@color/colourful_border_weak" android:width="2dp" />
|
||||
|
||||
</shape>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<corners android:radius="24dp"/>
|
||||
<solid android:color="@color/shape_red_rectangle"/>
|
||||
</shape>
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
|
||||
<solid android:color="#23262F" />
|
||||
|
||||
<corners android:radius="12dp" />
|
||||
|
||||
</shape>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<corners android:radius="24dp"/>
|
||||
<solid android:color="@color/colourful_ic_strong"/>
|
||||
</shape>
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="#4D4DCFE1"/>
|
||||
<stroke android:color="#4DCFE1" android:width="1px"/>
|
||||
<corners android:radius="24dp" />
|
||||
</shape>
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 按钮的选择器,可以设置按钮在不同状态下的时候,按钮不同的颜色 -->
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||
<item android:state_checked="true" android:drawable="@drawable/thumb_on" />
|
||||
<item android:drawable="@drawable/thumb_off" />
|
||||
</selector>
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle" >
|
||||
|
||||
<size android:height="26dp" android:width="26dp"/>
|
||||
<corners android:radius="13dp"/>
|
||||
<solid android:color="#747A8C"/>
|
||||
<stroke android:width="2dp"
|
||||
android:color="#23262F"/>
|
||||
</shape>
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
|
||||
<!-- 高度40 -->
|
||||
<size
|
||||
android:width="26dp"
|
||||
android:height="26dp" />
|
||||
<!-- 圆角弧度 20 -->
|
||||
<corners android:radius="13dp" />
|
||||
<solid android:color="#FCFCFD" />
|
||||
<stroke android:width="2dp"
|
||||
android:color="#4DCFE1"/>
|
||||
</shape>
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 底层下滑条的样式选择器,可控制Switch在不同状态下,底下下滑条的颜色 -->
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||
<item android:state_checked="true" android:drawable="@drawable/track_on" />
|
||||
<item android:drawable="@drawable/track_off" />
|
||||
|
||||
</selector>
|
||||
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle" >
|
||||
|
||||
<!-- 高度30 此处设置宽度无效-->
|
||||
<size android:height="26dp"/>
|
||||
<!-- 圆角弧度 15 -->
|
||||
<corners android:radius="13dp"/>
|
||||
<!-- 变化率 定义从左到右的颜色不变 -->
|
||||
<solid android:color="#23262F"/>
|
||||
|
||||
|
||||
</shape>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
|
||||
|
||||
<size android:height="26dp"
|
||||
android:width="50dp"/>
|
||||
<corners android:radius="13dp" />
|
||||
<solid android:height="26dp" android:color="#4DCFE1" />
|
||||
</shape>
|
||||
@@ -0,0 +1,161 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#1C1D22">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_back"
|
||||
android:layout_width="26dp"
|
||||
android:layout_height="26dp"
|
||||
android:layout_marginStart="20dp"
|
||||
android:src="@drawable/ic_live_action_bar_back"
|
||||
app:layout_constraintBottom_toBottomOf="@id/tv_title"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/tv_title" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="44dp"
|
||||
android:layout_marginTop="44dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/app_info"
|
||||
android:textColor="#FCFCFD"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_app_id_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="40dp"
|
||||
android:layout_marginStart="20dp"
|
||||
android:text="@string/app_info_app_id"
|
||||
android:textColor="#FCFCFD"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/tv_title" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/et_app_id"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:digits="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-_"
|
||||
android:hint="@string/interact_live_user_id_hint"
|
||||
android:maxLines="1"
|
||||
android:singleLine="true"
|
||||
android:textColor="#E6E7EC"
|
||||
android:textColorHint="#E6E7EC"
|
||||
android:theme="@style/AUIEditText"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="@id/tv_app_id_title"
|
||||
app:layout_constraintTop_toBottomOf="@id/tv_app_id_title" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_app_id_clear"
|
||||
android:layout_width="18dp"
|
||||
android:layout_height="18dp"
|
||||
android:src="@drawable/ic_scan_icon"
|
||||
app:layout_constraintBottom_toBottomOf="@id/et_app_id"
|
||||
app:layout_constraintEnd_toEndOf="@id/et_app_id"
|
||||
app:layout_constraintTop_toTopOf="@id/et_app_id" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_app_key"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="30dp"
|
||||
android:text="@string/app_info_app_key"
|
||||
android:textColor="#FCFCFD"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintStart_toStartOf="@id/tv_app_id_title"
|
||||
app:layout_constraintTop_toBottomOf="@id/et_app_id" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/et_app_key"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:digits="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-_"
|
||||
android:hint="@string/interact_live_user_id_hint"
|
||||
android:maxLines="1"
|
||||
android:singleLine="true"
|
||||
android:textColor="#E6E7EC"
|
||||
android:textColorHint="#E6E7EC"
|
||||
android:theme="@style/AUIEditText"
|
||||
app:layout_constraintEnd_toEndOf="@id/et_app_id"
|
||||
app:layout_constraintStart_toStartOf="@id/tv_app_key"
|
||||
app:layout_constraintTop_toBottomOf="@id/tv_app_key" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_app_key_clear"
|
||||
android:layout_width="18dp"
|
||||
android:layout_height="18dp"
|
||||
android:src="@drawable/ic_scan_icon"
|
||||
app:layout_constraintBottom_toBottomOf="@id/et_app_key"
|
||||
app:layout_constraintEnd_toEndOf="@id/et_app_key"
|
||||
app:layout_constraintTop_toTopOf="@id/et_app_key" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_play_domain_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="30dp"
|
||||
android:text="@string/app_info_play_domain"
|
||||
android:textColor="#FCFCFD"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintStart_toStartOf="@id/tv_app_id_title"
|
||||
app:layout_constraintTop_toBottomOf="@id/et_app_key" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/et_play_domain"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:digits="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-_"
|
||||
android:hint="@string/interact_live_user_id_hint"
|
||||
android:maxLines="1"
|
||||
android:singleLine="true"
|
||||
android:textColor="#E6E7EC"
|
||||
android:textColorHint="#E6E7EC"
|
||||
android:theme="@style/AUIEditText"
|
||||
app:layout_constraintEnd_toEndOf="@id/et_app_id"
|
||||
app:layout_constraintStart_toStartOf="@id/tv_app_id_title"
|
||||
app:layout_constraintTop_toBottomOf="@id/tv_play_domain_title" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_play_domain_clear"
|
||||
android:layout_width="18dp"
|
||||
android:layout_height="18dp"
|
||||
android:src="@drawable/ic_scan_icon"
|
||||
app:layout_constraintBottom_toBottomOf="@id/et_play_domain"
|
||||
app:layout_constraintEnd_toEndOf="@id/et_play_domain"
|
||||
app:layout_constraintTop_toTopOf="@id/et_play_domain" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_confirm"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:layout_gravity="bottom|center_horizontal"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:layout_marginBottom="44dp"
|
||||
android:background="@drawable/shape_rect_blue"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center"
|
||||
android:includeFontPadding="false"
|
||||
android:maxLines="2"
|
||||
android:clickable="false"
|
||||
android:text="@string/interact_live_next"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="14sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -0,0 +1,235 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#1C1D22">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_back"
|
||||
android:layout_width="26dp"
|
||||
android:layout_height="26dp"
|
||||
android:layout_marginStart="20dp"
|
||||
android:src="@drawable/ic_live_action_bar_back"
|
||||
app:layout_constraintBottom_toBottomOf="@id/tv_title"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/tv_title" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="44dp"
|
||||
android:layout_marginTop="44dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/interact_live"
|
||||
android:textColor="#FCFCFD"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_setting"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="44dp"
|
||||
android:layout_marginTop="44dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/interact_setting"
|
||||
android:textColor="#FCFCFD"
|
||||
android:textSize="12sp"
|
||||
android:visibility="visible"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginTop="40dp"
|
||||
app:layout_constraintBottom_toTopOf="@id/tv_confirm"
|
||||
app:layout_constraintTop_toBottomOf="@id/tv_title">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_user_id_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="20dp"
|
||||
android:text="@string/interact_live_user_id"
|
||||
android:textColor="#FCFCFD"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/et_user_id"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:digits="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
android:hint="@string/interact_live_user_id_hint"
|
||||
android:maxLength="20"
|
||||
android:maxLines="1"
|
||||
android:singleLine="true"
|
||||
android:textColor="#E6E7EC"
|
||||
android:textColorHint="#E6E7EC"
|
||||
android:theme="@style/AUIEditText"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="@id/tv_user_id_title"
|
||||
app:layout_constraintTop_toBottomOf="@id/tv_user_id_title" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_user_id_clear"
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp"
|
||||
android:padding="8dp"
|
||||
android:src="@drawable/ic_close"
|
||||
app:layout_constraintBottom_toBottomOf="@id/et_user_id"
|
||||
app:layout_constraintEnd_toEndOf="@id/et_user_id"
|
||||
app:layout_constraintTop_toTopOf="@id/et_user_id" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_room_number"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="30dp"
|
||||
android:text="@string/interact_live_room_id"
|
||||
android:textColor="#FCFCFD"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintStart_toStartOf="@id/tv_user_id_title"
|
||||
app:layout_constraintTop_toBottomOf="@id/et_user_id" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/et_room_id"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:digits="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
android:hint="@string/interact_live_user_id_hint"
|
||||
android:maxLength="20"
|
||||
android:maxLines="1"
|
||||
android:singleLine="true"
|
||||
android:textColor="#E6E7EC"
|
||||
android:textColorHint="#E6E7EC"
|
||||
android:theme="@style/AUIEditText"
|
||||
app:layout_constraintEnd_toEndOf="@id/et_user_id"
|
||||
app:layout_constraintStart_toStartOf="@id/tv_user_id_title"
|
||||
app:layout_constraintTop_toBottomOf="@id/tv_room_number" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_room_id_clear"
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp"
|
||||
android:padding="8dp"
|
||||
android:src="@drawable/ic_close"
|
||||
app:layout_constraintBottom_toBottomOf="@id/et_room_id"
|
||||
app:layout_constraintEnd_toEndOf="@id/et_room_id"
|
||||
app:layout_constraintTop_toTopOf="@id/et_room_id" />
|
||||
|
||||
<fragment
|
||||
android:id="@+id/fragment_app_info"
|
||||
android:name="com.alivc.live.interactive_common.InteractLiveAppInfoFragment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toBottomOf="@id/et_room_id" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/role_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_marginTop="30dp"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:orientation="horizontal"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/fragment_app_info"
|
||||
tools:visibility="visible">
|
||||
|
||||
<com.alivc.live.interactive_common.widget.InteractLiveRadioButton
|
||||
android:id="@+id/radio_button_anchor"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
app:ilrb_text="@string/interact_live_anchor" />
|
||||
|
||||
<View
|
||||
android:layout_width="15dp"
|
||||
android:layout_height="1dp" />
|
||||
|
||||
<com.alivc.live.interactive_common.widget.InteractLiveRadioButton
|
||||
android:id="@+id/radio_button_audience"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
app:ilrb_text="@string/interact_live_audience" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="30dp"
|
||||
android:orientation="vertical"
|
||||
android:padding="10dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/role_container">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_url_entrance"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="#00000000"
|
||||
android:gravity="center"
|
||||
android:includeFontPadding="false"
|
||||
android:lineSpacingExtra="3dp"
|
||||
android:padding="10dp"
|
||||
android:singleLine="true"
|
||||
android:text="@string/tips_use_url_for_interactive"
|
||||
android:textColor="#FCFCFD"
|
||||
android:textSize="16dp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_interactive_url"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:includeFontPadding="false"
|
||||
android:lineSpacingExtra="3dp"
|
||||
android:padding="5dp"
|
||||
android:textColor="#FCFCFD"
|
||||
android:textSize="12dp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</ScrollView>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_confirm"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:layout_gravity="bottom|center_horizontal"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:layout_marginBottom="44dp"
|
||||
android:background="@drawable/shape_rect_blue"
|
||||
android:clickable="false"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center"
|
||||
android:includeFontPadding="false"
|
||||
android:maxLines="2"
|
||||
android:text="@string/interact_live_confirm"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="14sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -0,0 +1,299 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#1C1D22">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_back"
|
||||
android:layout_width="26dp"
|
||||
android:layout_height="26dp"
|
||||
android:layout_marginStart="20dp"
|
||||
android:src="@drawable/ic_live_action_bar_back"
|
||||
app:layout_constraintBottom_toBottomOf="@id/tv_title"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/tv_title" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="44dp"
|
||||
android:layout_marginTop="44dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/interact_setting"
|
||||
android:textColor="#FCFCFD"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<View
|
||||
android:id="@+id/view_divider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1px"
|
||||
android:background="#3A3D48"
|
||||
app:layout_constraintTop_toBottomOf="@id/tv_title" />
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginBottom="20dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/view_divider"
|
||||
app:layout_constraintBottom_toTopOf="@id/btn_commit">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/tab_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="45dp">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/tab_args_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tab_args_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_marginTop="17dp"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:text="@string/push_args"
|
||||
android:textColor="#FCFCFD"
|
||||
android:textSize="@dimen/font_size_28px" />
|
||||
|
||||
<View
|
||||
android:id="@+id/tab_args_view"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="3dp"
|
||||
android:layout_gravity="bottom|center_horizontal"
|
||||
android:background="#4DCFE1" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/tab_action_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tab_action_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_marginTop="17dp"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:text="@string/stream_pusher_tip"
|
||||
android:textColor="#FCFCFD"
|
||||
android:textSize="@dimen/font_size_28px" />
|
||||
|
||||
<View
|
||||
android:id="@+id/tab_action_view"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="3dp"
|
||||
android:layout_gravity="bottom|center_horizontal"
|
||||
android:background="#4DCFE1"
|
||||
android:visibility="invisible" />
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/multi_interactive_root"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintTop_toBottomOf="@id/view_divider">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginTop="3dp"
|
||||
android:layout_marginRight="10dp"
|
||||
android:text="@string/pull_multi_interact_name_tv"
|
||||
android:textColor="#FCFCFD"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<Switch
|
||||
android:id="@+id/multi_interaction_control"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_marginRight="16dp"
|
||||
android:checked="false"
|
||||
android:textOff=""
|
||||
android:textOn=""
|
||||
android:thumb="@drawable/thumb"
|
||||
android:track="@drawable/track" />
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0.5dp"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_marginTop="5dp"
|
||||
android:background="#3A3D48" />
|
||||
</RelativeLayout>
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginTop="3dp"
|
||||
android:layout_marginRight="10dp"
|
||||
android:text="@string/h5_compatible_name_tv"
|
||||
android:textColor="#FCFCFD"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<Switch
|
||||
android:id="@+id/h5_compatible_control"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_marginRight="16dp"
|
||||
android:checked="false"
|
||||
android:textOff=""
|
||||
android:textOn=""
|
||||
android:thumb="@drawable/thumb"
|
||||
android:track="@drawable/track" />
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0.5dp"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_marginTop="5dp"
|
||||
android:background="#3A3D48" />
|
||||
</RelativeLayout>
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginTop="3dp"
|
||||
android:layout_marginRight="10dp"
|
||||
android:text="@string/data_channel_name_tv"
|
||||
android:textColor="#FCFCFD"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<Switch
|
||||
android:id="@+id/data_channel_control"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_marginRight="16dp"
|
||||
android:checked="false"
|
||||
android:textOff=""
|
||||
android:textOn=""
|
||||
android:thumb="@drawable/thumb"
|
||||
android:track="@drawable/track" />
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0.5dp"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_marginTop="5dp"
|
||||
android:background="#3A3D48" />
|
||||
</RelativeLayout>
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginTop="3dp"
|
||||
android:layout_marginRight="10dp"
|
||||
android:text="@string/earback_open_without_headset"
|
||||
android:textColor="#FCFCFD"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<Switch
|
||||
android:id="@+id/earback_open_without_headset_control"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_marginRight="16dp"
|
||||
android:checked="false"
|
||||
android:textOff=""
|
||||
android:textOn=""
|
||||
android:thumb="@drawable/thumb"
|
||||
android:track="@drawable/track" />
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0.5dp"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_marginTop="5dp"
|
||||
android:background="#3A3D48" />
|
||||
</RelativeLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<com.alivc.live.commonui.configview.LivePushSettingView
|
||||
android:id="@+id/setting_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_commit"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:layout_marginBottom="20dp"
|
||||
android:background="@drawable/shape_pysh_btn_bg"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal"
|
||||
android:text="@string/interact_live_confirm"
|
||||
android:textColor="#FCFCFD"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="normal"
|
||||
app:layout_constraintBottom_toBottomOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -0,0 +1,124 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#1C1D22">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_app_info"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_marginTop="39dp"
|
||||
android:text="@string/app_info"
|
||||
android:textColor="#FCFCFD"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_arrow"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:padding="8dp"
|
||||
android:src="@drawable/ic_chevron_down"
|
||||
app:layout_constraintBottom_toBottomOf="@id/tv_app_info"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/tv_app_info" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/cl_app_info"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintEnd_toEndOf="@id/iv_arrow"
|
||||
app:layout_constraintStart_toStartOf="@id/tv_app_info"
|
||||
app:layout_constraintTop_toBottomOf="@id/tv_app_info"
|
||||
tools:visibility="visible">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_app_id_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/app_info_app_id"
|
||||
android:textColor="#B2B7C4"
|
||||
android:textSize="12sp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_app_id"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="#B2B7C4"
|
||||
android:textSize="12sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/tv_app_id_title" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_app_key_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:text="@string/app_info_app_key"
|
||||
android:textColor="#B2B7C4"
|
||||
android:textSize="12sp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/tv_app_id_title" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_app_key"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="#B2B7C4"
|
||||
android:textSize="12sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/tv_app_key_title" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_play_domain_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:text="@string/app_info_play_domain"
|
||||
android:textColor="#B2B7C4"
|
||||
android:textSize="12sp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/tv_app_key_title" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_play_domain"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="#B2B7C4"
|
||||
android:textSize="12sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/tv_play_domain_title" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_edit"
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="36dp"
|
||||
android:paddingVertical="10dp"
|
||||
android:src="@drawable/ic_edit"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/tv_play_domain" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<View
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginHorizontal="20dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:background="#3A3D48"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/cl_app_info" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="312dp"
|
||||
android:layout_height="128dp"
|
||||
android:background="@drawable/shape_interact_live_dialog_bg">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_tips"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="70dp"
|
||||
android:textColor="#FCFCFD"
|
||||
android:textSize="16sp"
|
||||
android:gravity="center"
|
||||
android:text="@string/interact_live_lost_connect"
|
||||
app:layout_constraintBottom_toBottomOf="@id/view_horizontal_line"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<View
|
||||
android:id="@+id/view_horizontal_line"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="#3A3D48"
|
||||
app:layout_constraintTop_toBottomOf="@id/tv_tips"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_confirm"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="48dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/interact_live_confirm"
|
||||
android:textColor="#4DCFE1"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/view_horizontal_line" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/root"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="120dp"
|
||||
android:background="@drawable/shape_interact_live_unselected">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_marginTop="12dp"
|
||||
android:textColor="#FCFCFD"
|
||||
android:textSize="14sp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="2dp"
|
||||
android:src="@drawable/ic_decorate"
|
||||
app:layout_constraintEnd_toEndOf="@id/tv_title"
|
||||
app:layout_constraintStart_toStartOf="@id/tv_title"
|
||||
app:layout_constraintTop_toBottomOf="@id/tv_title" />
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:layout_marginBottom="15dp"
|
||||
android:src="@drawable/ic_anchor"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -0,0 +1,96 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="312dp"
|
||||
android:layout_height="128dp"
|
||||
android:background="@drawable/shape_interact_live_dialog_bg">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_tips"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="#FCFCFD"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintBottom_toBottomOf="@id/view_horizontal_line"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/et_input"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="32dp"
|
||||
android:layout_marginStart="24dp"
|
||||
android:layout_marginEnd="24dp"
|
||||
android:background="@drawable/shape_interact_live_edit_bg"
|
||||
android:gravity="start|center_vertical"
|
||||
android:hint="@string/interact_live_connect_tips"
|
||||
android:maxLines="1"
|
||||
android:paddingStart="10dp"
|
||||
android:paddingEnd="0dp"
|
||||
android:textColor="#E6E7EC"
|
||||
android:textColorHint="#747A8C"
|
||||
android:textSize="14sp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="@id/view_horizontal_line"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_clear"
|
||||
android:layout_width="12dp"
|
||||
android:layout_height="12dp"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:src="@drawable/ic_close"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="@id/et_input"
|
||||
app:layout_constraintEnd_toEndOf="@id/et_input"
|
||||
app:layout_constraintTop_toTopOf="@id/et_input"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<View
|
||||
android:id="@+id/view_horizontal_line"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="#3A3D48"
|
||||
app:layout_constraintBottom_toTopOf="@id/tv_cancel"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_cancel"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="48dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/interact_live_cancel"
|
||||
android:textColor="#B2B7C4"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/view_vertical_line"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<View
|
||||
android:id="@+id/view_vertical_line"
|
||||
android:layout_width="1dp"
|
||||
android:layout_height="48dp"
|
||||
android:background="#3A3D48"
|
||||
app:layout_constraintEnd_toStartOf="@id/tv_confirm"
|
||||
app:layout_constraintStart_toEndOf="@id/tv_cancel"
|
||||
app:layout_constraintTop_toTopOf="@id/tv_cancel" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_confirm"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="48dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/interact_live_confirm"
|
||||
android:textColor="#4DCFE1"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/view_vertical_line" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -0,0 +1,258 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="312dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/shape_interact_live_dialog_bg">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_tips"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="24dp"
|
||||
android:layout_marginBottom="24dp"
|
||||
android:textColor="#FCFCFD"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintBottom_toBottomOf="@id/view_horizontal_line"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/et_input_user_id"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="32dp"
|
||||
android:layout_marginStart="24dp"
|
||||
android:layout_marginTop="24dp"
|
||||
android:layout_marginEnd="24dp"
|
||||
android:background="@drawable/shape_interact_live_edit_bg"
|
||||
android:digits="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
android:gravity="start|center_vertical"
|
||||
android:hint="@string/interact_live_connect_author_tips"
|
||||
android:maxLines="1"
|
||||
android:paddingStart="10dp"
|
||||
android:paddingEnd="0dp"
|
||||
android:textColor="#E6E7EC"
|
||||
android:textColorHint="#747A8C"
|
||||
android:textSize="14sp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_clear_user_id"
|
||||
android:layout_width="12dp"
|
||||
android:layout_height="12dp"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:src="@drawable/ic_close"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="@id/et_input_user_id"
|
||||
app:layout_constraintEnd_toEndOf="@id/et_input_user_id"
|
||||
app:layout_constraintTop_toTopOf="@id/et_input_user_id" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/et_input_room_id"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="32dp"
|
||||
android:layout_marginStart="24dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginEnd="24dp"
|
||||
android:layout_marginBottom="24dp"
|
||||
android:background="@drawable/shape_interact_live_edit_bg"
|
||||
android:digits="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
android:gravity="start|center_vertical"
|
||||
android:hint="@string/interact_live_connect_room_tips"
|
||||
android:maxLines="1"
|
||||
android:paddingStart="10dp"
|
||||
android:paddingEnd="0dp"
|
||||
android:textColor="#E6E7EC"
|
||||
android:textColorHint="#747A8C"
|
||||
android:textSize="14sp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/et_input_user_id"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_clear_room_id"
|
||||
android:layout_width="12dp"
|
||||
android:layout_height="12dp"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:src="@drawable/ic_close"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="@id/et_input_room_id"
|
||||
app:layout_constraintEnd_toEndOf="@id/et_input_room_id"
|
||||
app:layout_constraintTop_toTopOf="@id/et_input_room_id" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/et_input_url"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="32dp"
|
||||
android:layout_marginStart="24dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginEnd="24dp"
|
||||
android:layout_marginBottom="24dp"
|
||||
android:background="@drawable/shape_interact_live_edit_bg"
|
||||
android:digits="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
android:gravity="start|center_vertical"
|
||||
android:hint="@string/interact_live_url_connect_tips"
|
||||
android:maxLines="1"
|
||||
android:paddingStart="10dp"
|
||||
android:paddingEnd="0dp"
|
||||
android:textColor="#E6E7EC"
|
||||
android:textColorHint="#747A8C"
|
||||
android:textSize="14sp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/et_input_room_id"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_scan_qr"
|
||||
android:layout_width="12dp"
|
||||
android:layout_height="12dp"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:src="@drawable/ic_scan_icon"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="@id/et_input_url"
|
||||
app:layout_constraintEnd_toEndOf="@id/et_input_url"
|
||||
app:layout_constraintTop_toTopOf="@id/et_input_url" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/video_radio_group"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:paddingHorizontal="10dp"
|
||||
android:paddingVertical="5dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/et_input_url">
|
||||
|
||||
<TextView
|
||||
android:layout_width="100dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:text="@string/interact_live_video_stream_type"
|
||||
android:textColor="#FCFCFD" />
|
||||
|
||||
<RadioGroup
|
||||
android:id="@+id/rg_video_stream_type"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/rb_camera"
|
||||
android:layout_width="90dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="true"
|
||||
android:text="@string/interact_live_video_stream_type_camera"
|
||||
android:textColor="#FCFCFD" />
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/rb_screen"
|
||||
android:layout_width="90dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:text="@string/interact_live_video_stream_type_screen"
|
||||
android:textColor="#FCFCFD" />
|
||||
|
||||
</RadioGroup>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/audio_radio_group"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:paddingHorizontal="10dp"
|
||||
android:paddingVertical="5dp"
|
||||
app:layout_constraintBottom_toBottomOf="@id/view_horizontal_line"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/video_radio_group">
|
||||
|
||||
<TextView
|
||||
android:layout_width="100dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:text="@string/interact_live_audio_stream_type"
|
||||
android:textColor="#FCFCFD" />
|
||||
|
||||
<RadioGroup
|
||||
android:id="@+id/rg_audio_stream_type"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/rb_mic"
|
||||
android:layout_width="90dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="true"
|
||||
android:text="@string/interact_live_audio_stream_type_mic"
|
||||
android:textColor="#FCFCFD" />
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/rb_dual"
|
||||
android:layout_width="90dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/interact_live_audio_stream_type_dual"
|
||||
android:textColor="#FCFCFD" />
|
||||
|
||||
</RadioGroup>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:id="@+id/view_horizontal_line"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="#3A3D48"
|
||||
app:layout_constraintBottom_toTopOf="@id/tv_cancel"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_cancel"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="48dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/interact_live_cancel"
|
||||
android:textColor="#B2B7C4"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/view_vertical_line"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<View
|
||||
android:id="@+id/view_vertical_line"
|
||||
android:layout_width="1dp"
|
||||
android:layout_height="48dp"
|
||||
android:background="#3A3D48"
|
||||
app:layout_constraintEnd_toStartOf="@id/tv_confirm"
|
||||
app:layout_constraintStart_toEndOf="@id/tv_cancel"
|
||||
app:layout_constraintTop_toTopOf="@id/tv_cancel" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_confirm"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="48dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/interact_live_confirm"
|
||||
android:textColor="#4DCFE1"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/view_vertical_line" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<merge xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_horizontal">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/fl_un_connect"
|
||||
android:layout_width="95dp"
|
||||
android:layout_height="160dp"
|
||||
android:layout_marginBottom="15dp"
|
||||
android:background="#23262F">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:src="@drawable/ic_interact_live_un_connect_bg" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_connect"
|
||||
android:layout_width="94dp"
|
||||
android:layout_height="30dp"
|
||||
android:background="@drawable/shape_pysh_btn_bg"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center"
|
||||
android:includeFontPadding="false"
|
||||
android:maxLines="1"
|
||||
android:text="@string/interact_start_connect"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="16sp" />
|
||||
|
||||
</merge>
|
||||
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_mute_audio"
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp"
|
||||
android:padding="3dp"
|
||||
android:scaleType="centerCrop"
|
||||
android:src="@drawable/ic_interact_unmute" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_show_view"
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp"
|
||||
android:padding="3dp"
|
||||
android:scaleType="centerCrop"
|
||||
android:src="@drawable/ic_view_container_show" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_view_resize"
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp"
|
||||
android:padding="3dp"
|
||||
android:scaleType="centerCrop"
|
||||
android:src="@drawable/ic_half_screen" />
|
||||
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_interactive_pull_url"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:text="@string/interact_live_pull_url"
|
||||
android:textColor="#FCFCFD"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/et_interactive_pull_url"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="22dp"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:maxLines="1"
|
||||
android:singleLine="true"
|
||||
android:textColor="#E6E7EC"
|
||||
android:textColorHint="#E6E7EC"
|
||||
android:theme="@style/AUIEditText"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="@id/tv_interactive_pull_url"
|
||||
app:layout_constraintTop_toBottomOf="@id/tv_interactive_pull_url" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_qr_pull_url"
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp"
|
||||
android:padding="8dp"
|
||||
android:src="@drawable/ic_scan_icon"
|
||||
app:layout_constraintBottom_toBottomOf="@id/et_interactive_pull_url"
|
||||
app:layout_constraintEnd_toEndOf="@id/et_interactive_pull_url"
|
||||
app:layout_constraintTop_toTopOf="@id/et_interactive_pull_url" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -0,0 +1,57 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_camera"
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp"
|
||||
android:padding="3dp"
|
||||
android:src="@drawable/ic_camera" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_speaker_phone"
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:padding="3dp"
|
||||
android:scaleType="centerCrop"
|
||||
android:src="@drawable/ic_speaker_phone_off" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_mute_audio"
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:padding="3dp"
|
||||
android:src="@drawable/ic_audio_capture_on" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_mute_video"
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:padding="3dp"
|
||||
android:src="@drawable/ic_local_video_on" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_enable_audio"
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:padding="3dp"
|
||||
android:scaleType="centerCrop"
|
||||
android:src="@drawable/ic_audio_capture_on" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_enable_video"
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:padding="3dp"
|
||||
android:scaleType="centerCrop"
|
||||
android:src="@drawable/ic_local_video_on" />
|
||||
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_info"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="#FCFCFD"
|
||||
android:textSize="15dp" />
|
||||
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,76 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="pull_interact_name_tv">Connect Interact</string>
|
||||
<string name="pull_interact_name_url">Connect Input URL</string>
|
||||
<string name="pull_multi_interact_name_tv">Multi Connect Interact/PK</string>
|
||||
<string name="h5_compatible_name_tv">H5 Compatible</string>
|
||||
<string name="earback_open_without_headset">Earback Without Headset</string>
|
||||
<string name="data_channel_name_tv">Data Channel</string>
|
||||
<string name="external_audio_stream_tv">External Audio Stream</string>
|
||||
<string name="external_video_stream_tv">External Video Stream</string>
|
||||
<string name="dual_audio_stream">Push Dual Audio</string>
|
||||
<string name="screen_share_stream">Push Screen Share</string>
|
||||
<string name="change_resolution_and_fps">Change Resolution FPS</string>
|
||||
<string name="force_ear_monitor">Force Ear Monitor</string>
|
||||
<string name="push_interactive_setting_audio_only">Audio Only</string>
|
||||
<string name="push_interactive_setting_gop">GOP</string>
|
||||
<string name="push_interactive_setting_hardware_decode">Enable Hardware Decode</string>
|
||||
<string name="push_interactive_h265">H265</string>
|
||||
<string name="interact_live">Interact Live</string>
|
||||
<string name="interact_live_url_title">Interact Live URL</string>
|
||||
<string name="interact_live_push_url">Interact Live PUSH URL</string>
|
||||
<string name="interact_live_pull_url">Interact Live PULL URL</string>
|
||||
<string name="interact_start_push">Start Push</string>
|
||||
<string name="interact_stop_push">Stop Push</string>
|
||||
<string name="pk_live">PK Interaction</string>
|
||||
<string name="interact_setting">Interact Setting</string>
|
||||
<string name="interact_live_cancel">Cancel</string>
|
||||
<string name="interact_live_confirm">Confirm</string>
|
||||
<string name="interact_live_audio_stream_type">Audio Stream</string>
|
||||
<string name="interact_live_audio_stream_type_mic">Mic</string>
|
||||
<string name="interact_live_audio_stream_type_dual">Dual</string>
|
||||
<string name="interact_live_video_stream_type">Video Stream</string>
|
||||
<string name="interact_live_video_stream_type_camera">Camera</string>
|
||||
<string name="interact_live_video_stream_type_screen">Screen</string>
|
||||
<string name="interact_live_next">Next</string>
|
||||
<string name="interact_live_user_id">User ID</string>
|
||||
<string name="interact_live_room_id">Room ID</string>
|
||||
<string name="app_info">APP Info</string>
|
||||
<string name="app_info_app_id">App ID</string>
|
||||
<string name="app_info_app_key">App Key</string>
|
||||
<string name="app_info_play_domain">PlayDomain</string>
|
||||
<string name="interact_live_user_id_hint">Please enter letters and numbers</string>
|
||||
<string name="interact_live_anchor">anchor</string>
|
||||
<string name="interact_live_audience">audience</string>
|
||||
<string name="interact_input_limit">0/5</string>
|
||||
<string name="interact_start_connect">Connect</string>
|
||||
<string name="pk_start_connect">PK</string>
|
||||
<string name="interact_stop_connect">UnConnect</string>
|
||||
<string name="pk_stop_connect">EndPK</string>
|
||||
<string name="interact_live_connecting">Connecting</string>
|
||||
<string name="pk_live_connecting">Connecting</string>
|
||||
<string name="interact_live_connect_tips">Enter the User ID</string>
|
||||
<string name="interact_live_url_connect_tips">Enter the User URL</string>
|
||||
<string name="interact_live_connect_author_tips">Enter the author ID</string>
|
||||
<string name="interact_live_connect_room_tips">Enter the room ID</string>
|
||||
<string name="interact_live_connect_input_error_tips">Input content</string>
|
||||
<string name="interact_live_connect_finish_tips">End connect?</string>
|
||||
<string name="pk_live_connect_finish_tips">End PK?</string>
|
||||
<string name="interact_live_leave_room_tips">Leave room?</string>
|
||||
<string name="interact_live_viewer_left">Viewer Left</string>
|
||||
|
||||
<string name="interact_live_lost_connect">Disconnect the link, please re-enter the live room</string>
|
||||
<string name="extern_stream_main">Main Extern Stream</string>
|
||||
<string name="beauty_switch">Beauty</string>
|
||||
<string name="waiting_download_video_resources">waiting for download external video resources</string>
|
||||
<string name="interact_live_setting_resolution">Resolution</string>
|
||||
<string name="target_bitrate">Target bitrate</string>
|
||||
<string name="target_rate_value">1200</string>
|
||||
<string name="captrue_fps">Captrue fps</string>
|
||||
<string name="init_fps">20</string>
|
||||
<string name="min_fps">Min fps</string>
|
||||
|
||||
<string name="live_push_switch_to_facing_back_camera">Switch to facing back camera,before scanning code</string>
|
||||
<string name="tips_use_url_for_interactive">Use URL for interactive live</string>
|
||||
<string name="tips_interactive_url_use_different_sdkappid">The sdkAppId used in the URL is different from the one in the App Info. Please note that this may cause pulling stream failure!</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,75 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="pull_interact_name_tv">连麦互动</string>
|
||||
<string name="pull_interact_name_url">推拉裸流</string>
|
||||
<string name="pull_multi_interact_name_tv">多人连麦/PK</string>
|
||||
<string name="h5_compatible_name_tv">H5兼容模式</string>
|
||||
<string name="earback_open_without_headset">强制耳返</string>
|
||||
<string name="data_channel_name_tv">DataChannel消息通道</string>
|
||||
<string name="external_audio_stream_tv">自定义音频</string>
|
||||
<string name="external_video_stream_tv">自定义视频</string>
|
||||
<string name="dual_audio_stream">推音频辅流</string>
|
||||
<string name="screen_share_stream">推屏幕共享流</string>
|
||||
<string name="change_resolution_and_fps">切换分辨率与FPS</string>
|
||||
<string name="force_ear_monitor">强制耳返</string>
|
||||
<string name="push_interactive_setting_audio_only">纯音频</string>
|
||||
<string name="push_interactive_setting_gop">GOP</string>
|
||||
<string name="push_interactive_setting_hardware_decode">开启硬解</string>
|
||||
<string name="push_interactive_h265">H265</string>
|
||||
<string name="interact_live">连麦互动</string>
|
||||
<string name="interact_live_url_title">推拉裸流配置</string>
|
||||
<string name="interact_live_push_url">请输入推流地址</string>
|
||||
<string name="interact_live_pull_url">请输入拉流地址</string>
|
||||
<string name="interact_start_push">开始推流</string>
|
||||
<string name="interact_stop_push">停止推流</string>
|
||||
<string name="pk_live">PK互动</string>
|
||||
<string name="interact_setting">参数设置</string>
|
||||
<string name="interact_live_cancel">取消</string>
|
||||
<string name="interact_live_confirm">确认</string>
|
||||
<string name="interact_live_audio_stream_type">音频流类型</string>
|
||||
<string name="interact_live_audio_stream_type_mic">麦克风流</string>
|
||||
<string name="interact_live_audio_stream_type_dual">音频辅流</string>
|
||||
<string name="interact_live_video_stream_type">视频流类型</string>
|
||||
<string name="interact_live_video_stream_type_camera">摄像头流</string>
|
||||
<string name="interact_live_video_stream_type_screen">屏幕共享</string>
|
||||
<string name="interact_live_next">下一步</string>
|
||||
<string name="interact_live_user_id">用户 ID</string>
|
||||
<string name="interact_live_room_id">房间号</string>
|
||||
<string name="app_info">应用信息</string>
|
||||
<string name="app_info_app_id">App ID</string>
|
||||
<string name="app_info_app_key">App Key</string>
|
||||
<string name="app_info_play_domain">播流域名</string>
|
||||
<string name="interact_live_user_id_hint">请输入字母、数字</string>
|
||||
<string name="interact_live_anchor">主播</string>
|
||||
<string name="interact_live_audience">观众</string>
|
||||
<string name="interact_input_limit">0/5</string>
|
||||
<string name="interact_start_connect">开始连麦</string>
|
||||
<string name="pk_start_connect">开始PK</string>
|
||||
<string name="interact_stop_connect">结束连麦</string>
|
||||
<string name="pk_stop_connect">结束PK</string>
|
||||
<string name="interact_live_connect_tips">请输入观众的用户ID</string>
|
||||
<string name="interact_live_url_connect_tips">请输入连麦观众的 URL</string>
|
||||
<string name="interact_live_connect_author_tips">请输入主播的用户ID</string>
|
||||
<string name="interact_live_connect_room_tips">请输入主播的房间号</string>
|
||||
<string name="interact_live_connect_input_error_tips">请输入内容</string>
|
||||
<string name="interact_live_connecting">正在连麦</string>
|
||||
<string name="pk_live_connecting">正在PK</string>
|
||||
<string name="interact_live_connect_finish_tips">确认要结束本次连麦吗?</string>
|
||||
<string name="pk_live_connect_finish_tips">确认要结束本次 PK 吗?</string>
|
||||
<string name="interact_live_leave_room_tips">确认要退出房间吗?</string>
|
||||
<string name="interact_live_viewer_left">观众已离开,结束连麦</string>
|
||||
<string name="interact_live_lost_connect">断开链接,请重新进入直播间</string>
|
||||
<string name="extern_stream_main">外部音视频</string>
|
||||
<string name="beauty_switch">美颜</string>
|
||||
<string name="waiting_download_video_resources">正在下载外部音视频资源中,请等待</string>
|
||||
<string name="interact_live_setting_resolution">分辨率</string>
|
||||
<string name="target_bitrate">视频目标码率</string>
|
||||
<string name="target_rate_value">1200</string>
|
||||
<string name="captrue_fps">采集帧率</string>
|
||||
<string name="init_fps">20</string>
|
||||
<string name="min_fps">最小帧率</string>
|
||||
|
||||
<string name="live_push_switch_to_facing_back_camera">扫码前请切换到后置摄像头</string>
|
||||
<string name="tips_use_url_for_interactive">使用URL方式进行互动直播</string>
|
||||
<string name="tips_interactive_url_use_different_sdkappid">URL中使用的sdkAppId与应用信息中的不同,请注意这可能会导致拉流失败!</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<declare-styleable name="InteractLiveRadioButton">
|
||||
<attr name="ilrb_text" format="string" />
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="InteractiveQrEditView">
|
||||
<attr name="title" format="string" />
|
||||
</declare-styleable>
|
||||
</resources>
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="wheel_black">#000000</color>
|
||||
<color name="transparent">#50000000</color>
|
||||
<color name="alivc_color_black">#000</color>
|
||||
<color name="shape_red_rectangle">#F53F3F</color>
|
||||
</resources>
|
||||
@@ -0,0 +1,76 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="pull_interact_name_tv">Connect Interact</string>
|
||||
<string name="pull_interact_name_url">Connect Input URL</string>
|
||||
<string name="pull_multi_interact_name_tv">Multi Connect Interact/PK</string>
|
||||
<string name="h5_compatible_name_tv">H5 Compatible</string>
|
||||
<string name="earback_open_without_headset">Earback Without Headset</string>
|
||||
<string name="data_channel_name_tv">Data Channel</string>
|
||||
<string name="external_audio_stream_tv">External Audio Stream</string>
|
||||
<string name="external_video_stream_tv">External Video Stream</string>
|
||||
<string name="dual_audio_stream">Push Dual Audio</string>
|
||||
<string name="screen_share_stream">Push Screen Share</string>
|
||||
<string name="change_resolution_and_fps">Change Resolution FPS</string>
|
||||
<string name="force_ear_monitor">Force Ear Monitor</string>
|
||||
<string name="push_interactive_setting_audio_only">Audio Only</string>
|
||||
<string name="push_interactive_setting_gop">GOP</string>
|
||||
<string name="push_interactive_setting_hardware_decode">Enable Hardware Decode</string>
|
||||
<string name="push_interactive_h265">H265</string>
|
||||
<string name="interact_live">Interact Live</string>
|
||||
<string name="interact_live_url_title">Interact Live URL</string>
|
||||
<string name="interact_live_push_url">Interact Live PUSH URL</string>
|
||||
<string name="interact_live_pull_url">Interact Live PULL URL</string>
|
||||
<string name="interact_start_push">Start Push</string>
|
||||
<string name="interact_stop_push">Stop Push</string>
|
||||
<string name="pk_live">PK Interaction</string>
|
||||
<string name="interact_setting">Interact Setting</string>
|
||||
<string name="interact_live_cancel">Cancel</string>
|
||||
<string name="interact_live_confirm">Confirm</string>
|
||||
<string name="interact_live_audio_stream_type">Audio Stream</string>
|
||||
<string name="interact_live_audio_stream_type_mic">Mic</string>
|
||||
<string name="interact_live_audio_stream_type_dual">Dual</string>
|
||||
<string name="interact_live_video_stream_type">Video Stream</string>
|
||||
<string name="interact_live_video_stream_type_camera">Camera</string>
|
||||
<string name="interact_live_video_stream_type_screen">Screen</string>
|
||||
<string name="interact_live_next">Next</string>
|
||||
<string name="interact_live_user_id">User ID</string>
|
||||
<string name="interact_live_room_id">Room ID</string>
|
||||
<string name="app_info">APP Info</string>
|
||||
<string name="app_info_app_id">App ID</string>
|
||||
<string name="app_info_app_key">App Key</string>
|
||||
<string name="app_info_play_domain">PlayDomain</string>
|
||||
<string name="interact_live_user_id_hint">Please enter letters and numbers</string>
|
||||
<string name="interact_live_anchor">anchor</string>
|
||||
<string name="interact_live_audience">audience</string>
|
||||
<string name="interact_input_limit">0/5</string>
|
||||
<string name="interact_start_connect">Connect</string>
|
||||
<string name="pk_start_connect">PK</string>
|
||||
<string name="interact_stop_connect">UnConnect</string>
|
||||
<string name="pk_stop_connect">EndPK</string>
|
||||
<string name="interact_live_connecting">Connecting</string>
|
||||
<string name="pk_live_connecting">Connecting</string>
|
||||
<string name="interact_live_connect_tips">Enter the User ID</string>
|
||||
<string name="interact_live_url_connect_tips">Enter the User URL</string>
|
||||
<string name="interact_live_connect_author_tips">Enter the author ID</string>
|
||||
<string name="interact_live_connect_room_tips">Enter the room ID</string>
|
||||
<string name="interact_live_connect_input_error_tips">Input content</string>
|
||||
<string name="interact_live_connect_finish_tips">End connect?</string>
|
||||
<string name="pk_live_connect_finish_tips">End PK?</string>
|
||||
<string name="interact_live_leave_room_tips">Leave room?</string>
|
||||
<string name="interact_live_viewer_left">Viewer Left</string>
|
||||
|
||||
<string name="interact_live_lost_connect">Disconnect the link, please re-enter the live room</string>
|
||||
<string name="extern_stream_main">Main Extern Stream</string>
|
||||
<string name="beauty_switch">Beauty</string>
|
||||
<string name="waiting_download_video_resources">waiting for download external video resources</string>
|
||||
<string name="interact_live_setting_resolution">Resolution</string>
|
||||
<string name="target_bitrate">Target bitrate</string>
|
||||
<string name="target_rate_value">1200</string>
|
||||
<string name="captrue_fps">Captrue fps</string>
|
||||
<string name="init_fps">20</string>
|
||||
<string name="min_fps">Min fps</string>
|
||||
|
||||
<string name="live_push_switch_to_facing_back_camera">Switch to facing back camera,before scanning code</string>
|
||||
<string name="tips_use_url_for_interactive">Use URL for interactive live</string>
|
||||
<string name="tips_interactive_url_use_different_sdkappid">The sdkAppId used in the URL is different from the one in the App Info. Please note that this may cause pulling stream failure!</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,12 @@
|
||||
<resources>
|
||||
|
||||
<style name="AUIEditText">
|
||||
<item name="colorControlNormal">#3A3D48</item>
|
||||
<item name="colorControlActivated">#3A3D48</item>
|
||||
</style>
|
||||
|
||||
<style name="Push_SeekBar_Style"
|
||||
parent="Widget.AppCompat.SeekBar">
|
||||
<item name="colorAccent">#4DCFE1</item>
|
||||
</style>
|
||||
</resources>
|
||||
1
LiveInteractive/live_pk/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/build
|
||||
43
LiveInteractive/live_pk/build.gradle
Normal file
@@ -0,0 +1,43 @@
|
||||
plugins {
|
||||
id 'com.android.library'
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion androidCompileSdkVersion
|
||||
buildToolsVersion androidBuildToolsVersion
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion androidMinSdkVersion
|
||||
targetSdkVersion androidTargetSdkVersion
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
|
||||
consumerProguardFiles "consumer-rules.pro"
|
||||
|
||||
javaCompileOptions {
|
||||
annotationProcessorOptions {
|
||||
arguments = [AROUTER_MODULE_NAME :'interactivePK' ]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation externalAndroidDesign
|
||||
|
||||
implementation externalARouter
|
||||
annotationProcessor externalARouterCompiler
|
||||
|
||||
implementation project(':LiveInteractive:live_interactive_common')
|
||||
}
|
||||
0
LiveInteractive/live_pk/consumer-rules.pro
Normal file
26
LiveInteractive/live_pk/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.alivc.live.interactive_pk">
|
||||
|
||||
<application>
|
||||
<activity
|
||||
android:name="com.alivc.live.interactive_pk.PKLiveActivity"
|
||||
android:alwaysRetainTaskState="true"
|
||||
android:launchMode="singleTop"
|
||||
android:screenOrientation="portrait"
|
||||
android:theme="@style/Theme.AppCompat.Light.NoActionBar" />
|
||||
<activity
|
||||
android:name="com.alivc.live.interactive_pk.MultiPKLiveActivity"
|
||||
android:alwaysRetainTaskState="true"
|
||||
android:launchMode="singleTop"
|
||||
android:screenOrientation="portrait"
|
||||
android:theme="@style/Theme.AppCompat.Light.NoActionBar" />
|
||||
<activity
|
||||
android:name="com.alivc.live.interactive_pk.PKLiveInputActivity"
|
||||
android:alwaysRetainTaskState="true"
|
||||
android:launchMode="singleTop"
|
||||
android:screenOrientation="portrait"
|
||||
android:theme="@style/Theme.AppCompat.Light.NoActionBar" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@@ -0,0 +1,478 @@
|
||||
package com.alivc.live.interactive_pk;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
import androidx.recyclerview.widget.GridLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.alibaba.android.arouter.facade.annotation.Route;
|
||||
import com.alivc.live.commonbiz.testapi.EGLContextTest;
|
||||
import com.alivc.live.commonui.messageview.AutoScrollMessagesView;
|
||||
import com.alivc.live.commonui.utils.StatusBarUtil;
|
||||
import com.alivc.live.commonui.widgets.ResizableFrameLayout;
|
||||
import com.alivc.live.commonutils.ToastUtils;
|
||||
import com.alivc.live.interactive_common.InteractiveConstants;
|
||||
import com.alivc.live.interactive_common.bean.InteractiveUserData;
|
||||
import com.alivc.live.interactive_common.listener.ConnectionLostListener;
|
||||
import com.alivc.live.interactive_common.listener.InteractLivePushPullListener;
|
||||
import com.alivc.live.interactive_common.listener.InteractLiveTipsViewListener;
|
||||
import com.alivc.live.interactive_common.utils.InteractLiveIntent;
|
||||
import com.alivc.live.commonui.avdialog.AUILiveDialog;
|
||||
import com.alivc.live.interactive_common.widget.ConnectionLostTipsView;
|
||||
import com.alivc.live.interactive_common.widget.InteractiveCommonInputView;
|
||||
import com.alivc.live.interactive_common.widget.InteractiveRoomControlView;
|
||||
import com.alivc.live.interactive_common.widget.RoomAndUserInfoView;
|
||||
import com.alivc.live.player.annotations.AlivcLivePlayError;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 多人 PK 互动界面
|
||||
*/
|
||||
@Route(path = "/interactivePK/multiPKLive")
|
||||
public class MultiPKLiveActivity extends AppCompatActivity {
|
||||
|
||||
private static final String TAG = "MultiPKLiveActivity";
|
||||
|
||||
private boolean isScene16In = false;
|
||||
|
||||
private PKController mPKController;
|
||||
private AUILiveDialog mAUILiveDialog;
|
||||
private InteractLiveIntent mCurrentIntent;
|
||||
private ImageView mCloseImageView;
|
||||
private TextView mShowConnectTextView;
|
||||
|
||||
private ResizableFrameLayout mOwnerFrameLayout;
|
||||
private RoomAndUserInfoView mOwnerInfoView;
|
||||
|
||||
private RecyclerView mRecyclerView;
|
||||
private TextView mHomeIdTextView;
|
||||
|
||||
private MultiPKLiveRecyclerViewAdapter mMultiPKLiveRecyclerViewAdapter;
|
||||
private List<Boolean> mDataList = new ArrayList<>();
|
||||
private Map<String, Integer> mKeyPositionMap = new HashMap<>();
|
||||
private Map<String, InteractiveUserData> mUserDataMap = new HashMap<>();
|
||||
//当 item 划出屏幕时,防止 stop 事件调用导致 mKeyPositionMap 移除对应的 userKey,从而无法 resumeVideo
|
||||
private List<String> mDetachedUserKeyList = new ArrayList<>();
|
||||
//已经添加混流的 userId
|
||||
private List<String> mHasAddMixStreamUserId = new ArrayList<>();
|
||||
|
||||
//停止 PK 时需要的 roomId 和 userId
|
||||
private InteractiveUserData mStopPKUserData;
|
||||
private ConnectionLostTipsView mConnectionLostTipsView;
|
||||
private InteractiveRoomControlView mInteractiveRoomControlView;
|
||||
private AutoScrollMessagesView mSeiMessageView;
|
||||
|
||||
private InteractiveUserData mAnchorUserData;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
StatusBarUtil.translucent(this, Color.TRANSPARENT);
|
||||
|
||||
setContentView(R.layout.activity_multi_pklive);
|
||||
|
||||
// 是否是全屏16方场景
|
||||
isScene16In = getIntent().getBooleanExtra(InteractiveConstants.KEY_TYPE_SCENE_MULTI_PK_16IN, false);
|
||||
|
||||
initView();
|
||||
initListener();
|
||||
initData();
|
||||
}
|
||||
|
||||
private void initView() {
|
||||
mAUILiveDialog = new AUILiveDialog(this);
|
||||
mCloseImageView = findViewById(R.id.iv_close);
|
||||
|
||||
mOwnerFrameLayout = findViewById(R.id.frame_owner);
|
||||
if (isScene16In) {
|
||||
int widthPixel = dip2px(getBaseContext(), MultiPKLiveRecyclerViewAdapter.ITEM_CONTAINER_WIDTH_16IN);
|
||||
int heightPixel = dip2px(getBaseContext(), MultiPKLiveRecyclerViewAdapter.ITEM_CONTAINER_WIDTH_16IN / 9 * 16);
|
||||
ConstraintLayout.LayoutParams layoutParams = new ConstraintLayout.LayoutParams(widthPixel, heightPixel);
|
||||
mOwnerFrameLayout.setLayoutParams(layoutParams);
|
||||
}
|
||||
mOwnerInfoView = findViewById(R.id.owner_info_view);
|
||||
|
||||
mShowConnectTextView = findViewById(R.id.tv_show_connect);
|
||||
|
||||
mHomeIdTextView = findViewById(R.id.tv_home_id);
|
||||
mHomeIdTextView.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Thread thread = new Thread(() -> EGLContextTest.testGLContext());
|
||||
mSeiMessageView.appendMessage("Manual Create EGLContext: " + thread.getName());
|
||||
thread.start();
|
||||
}
|
||||
});
|
||||
|
||||
mConnectionLostTipsView = new ConnectionLostTipsView(this);
|
||||
|
||||
mInteractiveRoomControlView = findViewById(R.id.interactive_setting_view);
|
||||
mSeiMessageView = findViewById(R.id.sei_receive_view);
|
||||
|
||||
GridLayoutManager gridLayoutManager = new GridLayoutManager(this, isScene16In ? 4 : 2);
|
||||
gridLayoutManager.setOrientation(GridLayoutManager.HORIZONTAL);
|
||||
|
||||
mRecyclerView = findViewById(R.id.recycler_view);
|
||||
mRecyclerView.setLayoutManager(gridLayoutManager);
|
||||
mMultiPKLiveRecyclerViewAdapter = new MultiPKLiveRecyclerViewAdapter(isScene16In);
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
mDataList.add(false);
|
||||
}
|
||||
|
||||
mMultiPKLiveRecyclerViewAdapter.setData(mDataList);
|
||||
mRecyclerView.setAdapter(mMultiPKLiveRecyclerViewAdapter);
|
||||
}
|
||||
|
||||
private void initListener() {
|
||||
mInteractiveRoomControlView.setOnClickEventListener(new InteractiveRoomControlView.OnClickEventListener() {
|
||||
@Override
|
||||
public void onClickSwitchCamera() {
|
||||
mPKController.switchCamera();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClickSpeakerPhone(boolean enable) {
|
||||
mPKController.enableSpeakerPhone(enable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClickMuteAudio(boolean mute) {
|
||||
mPKController.setMute(mute);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClickEnableAudio(boolean enable) {
|
||||
mPKController.enableAudioCapture(enable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClickMuteVideo(boolean mute) {
|
||||
mPKController.muteLocalCamera(mute);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClickEnableVideo(boolean enable) {
|
||||
mPKController.enableLocalCamera(enable);
|
||||
}
|
||||
});
|
||||
|
||||
mConnectionLostTipsView.setConnectionLostListener(new ConnectionLostListener() {
|
||||
@Override
|
||||
public void onConfirm() {
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
finish();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
mCloseImageView.setOnClickListener(view -> {
|
||||
mCurrentIntent = InteractLiveIntent.INTENT_FINISH;
|
||||
showInteractLiveDialog(0, getResources().getString(R.string.interact_live_leave_room_tips), false);
|
||||
});
|
||||
|
||||
mMultiPKLiveRecyclerViewAdapter.setOnPKItemViewChangedListener(new MultiPKLiveRecyclerViewAdapter.OnPKItemViewChangedListener() {
|
||||
@Override
|
||||
public void onItemViewAttachedToWindow(int position) {
|
||||
String userKey = getUserKeyByPosition(position);
|
||||
if (TextUtils.isEmpty(userKey)) {
|
||||
return;
|
||||
}
|
||||
mDetachedUserKeyList.remove(userKey);
|
||||
InteractiveUserData userData = mUserDataMap.get(userKey);
|
||||
if (userData == null) {
|
||||
return;
|
||||
}
|
||||
MultiPKLiveRecyclerViewAdapter.MultiPKLiveViewHolder viewHolder = (MultiPKLiveRecyclerViewAdapter.MultiPKLiveViewHolder) mRecyclerView.findViewHolderForAdapterPosition(position);
|
||||
if (viewHolder != null) {
|
||||
boolean reCreated = mMultiPKLiveRecyclerViewAdapter.resetFrameLayout(position, viewHolder.getRenderFrameLayout().hashCode());
|
||||
if (reCreated) {
|
||||
mPKController.setPullView(userData, viewHolder.getRenderFrameLayout());
|
||||
}
|
||||
}
|
||||
Log.d(TAG, "onItemViewAttachedToWindow: userKey = " + userKey + " --- position = " + position);
|
||||
mPKController.resumeVideoPlaying(userKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemViewDetachedToWindow(int position) {
|
||||
String userKey = getUserKeyByPosition(position);
|
||||
if (TextUtils.isEmpty(userKey)) {
|
||||
return;
|
||||
}
|
||||
mDetachedUserKeyList.add(userKey);
|
||||
Log.d(TAG, "onItemViewDetachedToWindow: userKey = " + userKey + " --- position = " + position);
|
||||
mPKController.pauseVideoPlaying(userKey);
|
||||
}
|
||||
});
|
||||
|
||||
mMultiPKLiveRecyclerViewAdapter.setOnPKConnectClickListener(new MultiPKLiveRecyclerViewAdapter.OnPKItemClickListener() {
|
||||
@Override
|
||||
public void onPKConnectClick(int position) {
|
||||
String content = getUserKeyByPosition(position);
|
||||
mStopPKUserData = mUserDataMap.get(content);
|
||||
if (mStopPKUserData != null && mPKController.isPKing(mStopPKUserData)) {
|
||||
mCurrentIntent = InteractLiveIntent.INTENT_STOP_PULL;
|
||||
showInteractLiveDialog(position, getResources().getString(R.string.pk_live_connect_finish_tips), false);
|
||||
} else {
|
||||
showInteractLiveDialog(position, null, true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPKMuteClick(int position, boolean mute) {
|
||||
String userKey = getUserKeyByPosition(position);
|
||||
mPKController.mutePKAnchor(userKey, mute);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEmptyView(int position, boolean empty) {
|
||||
String userKey = getUserKeyByPosition(position);
|
||||
InteractiveUserData userData = mUserDataMap.get(userKey);
|
||||
if (empty) {
|
||||
mPKController.setPullView(userData, null);
|
||||
} else {
|
||||
MultiPKLiveRecyclerViewAdapter.MultiPKLiveViewHolder viewHolder = (MultiPKLiveRecyclerViewAdapter.MultiPKLiveViewHolder) mRecyclerView.findViewHolderForAdapterPosition(position);
|
||||
mPKController.setPullView(userData, viewHolder != null ? viewHolder.getRenderFrameLayout() : null);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void initData() {
|
||||
InteractiveUserData anchorUserData = (InteractiveUserData) getIntent().getSerializableExtra(InteractiveConstants.DATA_TYPE_INTERACTIVE_USER_DATA);
|
||||
|
||||
mPKController = new PKController(this, anchorUserData);
|
||||
mAnchorUserData = anchorUserData;
|
||||
|
||||
mPKController.setMultiPKLivePushPullListener(new InteractLivePushPullListener() {
|
||||
@Override
|
||||
public void onPullSuccess(InteractiveUserData userData) {
|
||||
super.onPullSuccess(userData);
|
||||
if (userData == null) {
|
||||
return;
|
||||
}
|
||||
String userKey = userData.getKey();
|
||||
//开始 PK 成功
|
||||
if (mKeyPositionMap.containsKey(userKey)) {
|
||||
MultiPKLiveRecyclerViewAdapter.MultiPKLiveViewHolder viewHolder = (MultiPKLiveRecyclerViewAdapter.MultiPKLiveViewHolder) mRecyclerView.findViewHolderForAdapterPosition(mKeyPositionMap.get(userKey));
|
||||
if (viewHolder != null && !mHasAddMixStreamUserId.contains(userData.userId)) {
|
||||
viewHolder.updateUserInfo(userData.channelId, userData.userId);
|
||||
mHasAddMixStreamUserId.add(userData.userId);
|
||||
//添加混流
|
||||
mPKController.addMultiPKLiveMixTranscoding(false, userData, viewHolder.getRenderFrameLayout());
|
||||
}
|
||||
changeFrameLayoutViewVisible(true, mKeyPositionMap.get(userKey));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPullError(InteractiveUserData userData, AlivcLivePlayError errorType, String errorMsg) {
|
||||
super.onPullError(userData, errorType, errorMsg);
|
||||
if (userData == null) {
|
||||
return;
|
||||
}
|
||||
if (errorType == AlivcLivePlayError.AlivcLivePlayErrorStreamStopped) {
|
||||
ToastUtils.show("用户号:" + userData.userId + " 房间号:" + userData.channelId + " 主播离开");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPullStop(InteractiveUserData userData) {
|
||||
super.onPullStop(userData);
|
||||
if (userData == null) {
|
||||
return;
|
||||
}
|
||||
String userKey = userData.getKey();
|
||||
//停止 PK
|
||||
if (mHasAddMixStreamUserId.contains(userData.userId)) {
|
||||
//删除对应的混流
|
||||
mHasAddMixStreamUserId.remove(userData.userId);
|
||||
mPKController.removeMultiPKLiveMixTranscoding(false, userData);
|
||||
|
||||
if (mKeyPositionMap.containsKey(userKey) && mKeyPositionMap.get(userKey) != null) {
|
||||
Integer position = mKeyPositionMap.get(userKey);
|
||||
MultiPKLiveRecyclerViewAdapter.MultiPKLiveViewHolder viewHolder = (MultiPKLiveRecyclerViewAdapter.MultiPKLiveViewHolder) mRecyclerView.findViewHolderForAdapterPosition(position);
|
||||
if (viewHolder != null) {
|
||||
viewHolder.getRenderFrameLayout().removeAllViews();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if (mKeyPositionMap.containsKey(userKey) && !mDetachedUserKeyList.contains(userKey)) {
|
||||
changeFrameLayoutViewVisible(false, mKeyPositionMap.get(userKey));
|
||||
}
|
||||
mKeyPositionMap.remove(userKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectionLost() {
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mConnectionLostTipsView.show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPushSuccess() {
|
||||
super.onPushSuccess();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPushError() {
|
||||
super.onPushError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceiveSEIMessage(int payload, byte[] data) {
|
||||
super.onReceiveSEIMessage(payload, data);
|
||||
mSeiMessageView.appendMessage("[rtc] payload=" + payload + ", " + new String(data, StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceiveSEIDelay(String src, String type, String msg) {
|
||||
super.onReceiveSEIDelay(src, type, msg);
|
||||
mSeiMessageView.appendMessage("[" + src + "][" + type + "][" + msg + "ms]");
|
||||
}
|
||||
});
|
||||
|
||||
mPKController.startPush(mOwnerFrameLayout);
|
||||
|
||||
mPKController.addMultiPKLiveMixTranscoding(true, mPKController.mOwnerUserData, null);
|
||||
|
||||
mHomeIdTextView.setText(anchorUserData.channelId);
|
||||
mOwnerInfoView.setUserData(anchorUserData);
|
||||
}
|
||||
|
||||
private void showInteractLiveDialog(int position, String content, boolean showInputView) {
|
||||
InteractiveCommonInputView commonInputView = new InteractiveCommonInputView(MultiPKLiveActivity.this);
|
||||
commonInputView.setViewType(InteractiveCommonInputView.ViewType.PK);
|
||||
commonInputView.showInputView(content, showInputView);
|
||||
commonInputView.setDefaultRoomId(mAnchorUserData != null ? mAnchorUserData.channelId : "");
|
||||
mAUILiveDialog.setContentView(commonInputView);
|
||||
mAUILiveDialog.show();
|
||||
|
||||
commonInputView.setOnInteractLiveTipsViewListener(new InteractLiveTipsViewListener() {
|
||||
@Override
|
||||
public void onCancel() {
|
||||
if (mAUILiveDialog.isShowing()) {
|
||||
mAUILiveDialog.dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfirm() {
|
||||
//退出直播
|
||||
if (mCurrentIntent == InteractLiveIntent.INTENT_FINISH) {
|
||||
finish();
|
||||
} else if (mCurrentIntent == InteractLiveIntent.INTENT_STOP_PULL) {
|
||||
mAUILiveDialog.dismiss();
|
||||
if (mStopPKUserData != null) {
|
||||
mPKController.stopMultiPK(mStopPKUserData);
|
||||
}
|
||||
changeFrameLayoutViewVisible(false, position);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInputConfirm(InteractiveUserData userData) {
|
||||
mAUILiveDialog.dismiss();
|
||||
if (userData == null || TextUtils.isEmpty(userData.channelId) || TextUtils.isEmpty(userData.userId)) {
|
||||
return;
|
||||
}
|
||||
String userDataKey = userData.getKey();
|
||||
if (mKeyPositionMap.containsKey(userDataKey)) {
|
||||
return;
|
||||
}
|
||||
//同一个用户 id 不能重复 PK。
|
||||
if (isInPK(userData.userId)) {
|
||||
return;
|
||||
}
|
||||
RecyclerView.LayoutManager layoutManager = mRecyclerView.getLayoutManager();
|
||||
if (layoutManager != null) {
|
||||
layoutManager.scrollToPosition(position);
|
||||
}
|
||||
mRecyclerView.post(() -> {
|
||||
MultiPKLiveRecyclerViewAdapter.MultiPKLiveViewHolder viewHolder = (MultiPKLiveRecyclerViewAdapter.MultiPKLiveViewHolder) mRecyclerView.findViewHolderForAdapterPosition(position);
|
||||
if (viewHolder != null) {
|
||||
mKeyPositionMap.put(userDataKey, position);
|
||||
mUserDataMap.put(userDataKey, userData);
|
||||
mPKController.setMultiPKOtherInfo(userData);
|
||||
mPKController.startMultiPK(userData, viewHolder.getRenderFrameLayout());
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void changeFrameLayoutViewVisible(boolean isShowSurfaceView, int position) {
|
||||
mDataList.set(position, isShowSurfaceView);
|
||||
if (isShowSurfaceView) {
|
||||
mShowConnectTextView.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
mShowConnectTextView.setVisibility(View.GONE);
|
||||
}
|
||||
mMultiPKLiveRecyclerViewAdapter.notifyItemChanged(position, "payload");
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断该用户 id 是否在 PK 中
|
||||
*
|
||||
* @param userId 用户 ID
|
||||
*/
|
||||
private boolean isInPK(String userId) {
|
||||
return mKeyPositionMap.containsKey(userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
mPKController.release();
|
||||
}
|
||||
|
||||
private String getUserKeyByPosition(int position) {
|
||||
String content = "";
|
||||
for (String key : mKeyPositionMap.keySet()) {
|
||||
Integer value = mKeyPositionMap.get(key);
|
||||
if (value != null && position == value) {
|
||||
content = key;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将dip或dp值转换为px值,保证尺寸大小不变
|
||||
*
|
||||
* @param dipValue
|
||||
* @param dipValue DisplayMetrics类中属性density
|
||||
* @return
|
||||
*/
|
||||
private static int dip2px(Context context, float dipValue) {
|
||||
if (context == null || context.getResources() == null)
|
||||
return 1;
|
||||
final float scale = context.getResources().getDisplayMetrics().density;
|
||||
return (int) (dipValue * scale + 0.5f);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
package com.alivc.live.interactive_pk;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.alivc.live.commonui.widgets.ResizableFrameLayout;
|
||||
import com.alivc.live.interactive_common.widget.InteractivePaneControlView;
|
||||
import com.alivc.live.interactive_common.widget.RoomAndUserInfoView;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class MultiPKLiveRecyclerViewAdapter extends RecyclerView.Adapter<MultiPKLiveRecyclerViewAdapter.MultiPKLiveViewHolder> {
|
||||
private final Map<Integer, Integer> mFrameLayoutWithPositionMap = new HashMap<>();
|
||||
|
||||
private OnPKItemClickListener mListener;
|
||||
private List<Boolean> mData;
|
||||
private OnPKItemViewChangedListener mOnPKItemViewChangedListener;
|
||||
|
||||
private boolean isScene16In = false;
|
||||
|
||||
public static final float ITEM_CONTAINER_WIDTH_16IN = 80f;
|
||||
|
||||
public MultiPKLiveRecyclerViewAdapter(boolean scene16In) {
|
||||
isScene16In = scene16In;
|
||||
}
|
||||
|
||||
public boolean resetFrameLayout(int position, int frameLayoutHashCode) {
|
||||
if (mFrameLayoutWithPositionMap.containsKey(position)) {
|
||||
if (frameLayoutHashCode == mFrameLayoutWithPositionMap.get(position)) {
|
||||
return false;
|
||||
} else {
|
||||
mFrameLayoutWithPositionMap.put(position, frameLayoutHashCode);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewAttachedToWindow(MultiPKLiveRecyclerViewAdapter.MultiPKLiveViewHolder holder) {
|
||||
super.onViewAttachedToWindow(holder);
|
||||
if (mOnPKItemViewChangedListener != null) {
|
||||
mOnPKItemViewChangedListener.onItemViewAttachedToWindow(holder.getAdapterPosition());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewDetachedFromWindow(MultiPKLiveRecyclerViewAdapter.MultiPKLiveViewHolder holder) {
|
||||
super.onViewDetachedFromWindow(holder);
|
||||
if (mOnPKItemViewChangedListener != null) {
|
||||
mOnPKItemViewChangedListener.onItemViewDetachedToWindow(holder.getAdapterPosition());
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public MultiPKLiveViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_multi_pklive_item, parent, false);
|
||||
return new MultiPKLiveViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull MultiPKLiveViewHolder holder, int position) {
|
||||
Boolean isPKing = mData.get(position);
|
||||
holder.getRenderFrameLayout().setVisibility(isPKing ? View.VISIBLE : View.INVISIBLE);
|
||||
holder.getUnConnectFrameLayout().setVisibility(isPKing ? View.INVISIBLE : View.VISIBLE);
|
||||
if (isPKing) {
|
||||
holder.getConnectView().setText(holder.itemView.getContext().getResources().getString(R.string.pk_stop_connect));
|
||||
holder.getConnectView().setBackground(holder.itemView.getContext().getResources().getDrawable(R.drawable.shape_interact_live_un_connect_btn_bg));
|
||||
} else {
|
||||
holder.getConnectView().setText(holder.itemView.getContext().getResources().getString(R.string.pk_start_connect));
|
||||
holder.getConnectView().setBackground(holder.itemView.getContext().getResources().getDrawable(R.drawable.shape_pysh_btn_bg));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return mData == null ? 0 : mData.size();
|
||||
}
|
||||
|
||||
public void setOnPKConnectClickListener(OnPKItemClickListener listener) {
|
||||
this.mListener = listener;
|
||||
}
|
||||
|
||||
public void setOnPKItemViewChangedListener(OnPKItemViewChangedListener listener) {
|
||||
this.mOnPKItemViewChangedListener = listener;
|
||||
}
|
||||
|
||||
public void setData(List<Boolean> dataList) {
|
||||
this.mData = dataList;
|
||||
}
|
||||
|
||||
public class MultiPKLiveViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
private final RoomAndUserInfoView mUserInfoView;
|
||||
private InteractivePaneControlView mUserCtrlView;
|
||||
private final ResizableFrameLayout mOtherFrameLayout;
|
||||
private final FrameLayout mUnConnectFrameLayout;
|
||||
private final TextView mConnectTextView;
|
||||
|
||||
public MultiPKLiveViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
mUserInfoView = itemView.findViewById(R.id.view_userinfo);
|
||||
|
||||
mUserCtrlView = itemView.findViewById(R.id.view_ctrl);
|
||||
mUserCtrlView.enableMute(true);
|
||||
mUserCtrlView.setOnClickEventListener(new InteractivePaneControlView.OnClickEventListener() {
|
||||
@Override
|
||||
public void onClickMuteAudio(boolean mute) {
|
||||
if (mListener != null) {
|
||||
mListener.onPKMuteClick(getAdapterPosition(), mute);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClickEmptyView(boolean empty) {
|
||||
if (mListener != null) {
|
||||
mListener.onEmptyView(getAdapterPosition(), empty);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClickResize(boolean resize) {
|
||||
mOtherFrameLayout.resize();
|
||||
}
|
||||
});
|
||||
|
||||
mOtherFrameLayout = itemView.findViewById(R.id.render_container);
|
||||
mUnConnectFrameLayout = itemView.findViewById(R.id.fl_un_connect);
|
||||
|
||||
mConnectTextView = itemView.findViewById(R.id.tv_connect);
|
||||
if (isScene16In) {
|
||||
mConnectTextView.setVisibility(View.GONE);
|
||||
|
||||
int widthPixel = dip2px(itemView.getContext(), ITEM_CONTAINER_WIDTH_16IN);
|
||||
int heightPixel = dip2px(itemView.getContext(), ITEM_CONTAINER_WIDTH_16IN / 9 * 16);
|
||||
ConstraintLayout.LayoutParams layoutParams = new ConstraintLayout.LayoutParams(widthPixel, heightPixel);
|
||||
mOtherFrameLayout.setLayoutParams(layoutParams);
|
||||
|
||||
itemView.setOnClickListener(view -> {
|
||||
connectPK();
|
||||
});
|
||||
} else {
|
||||
mConnectTextView.setOnClickListener(view -> {
|
||||
connectPK();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void updateUserInfo(String channelId, String userId) {
|
||||
mUserInfoView.setUserInfo(channelId, userId);
|
||||
}
|
||||
|
||||
public FrameLayout getRenderFrameLayout() {
|
||||
return mOtherFrameLayout;
|
||||
}
|
||||
|
||||
public FrameLayout getUnConnectFrameLayout() {
|
||||
return mUnConnectFrameLayout;
|
||||
}
|
||||
|
||||
public TextView getConnectView() {
|
||||
return mConnectTextView;
|
||||
}
|
||||
|
||||
private void connectPK() {
|
||||
if (mListener != null) {
|
||||
mFrameLayoutWithPositionMap.put(getAdapterPosition(), mOtherFrameLayout.hashCode());
|
||||
mListener.onPKConnectClick(getAdapterPosition());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将dip或dp值转换为px值,保证尺寸大小不变
|
||||
*
|
||||
* @param dipValue
|
||||
* @param dipValue DisplayMetrics类中属性density
|
||||
* @return
|
||||
*/
|
||||
private static int dip2px(Context context, float dipValue) {
|
||||
if (context == null || context.getResources() == null)
|
||||
return 1;
|
||||
final float scale = context.getResources().getDisplayMetrics().density;
|
||||
return (int) (dipValue * scale + 0.5f);
|
||||
}
|
||||
|
||||
public interface OnPKItemClickListener {
|
||||
void onPKConnectClick(int position);
|
||||
|
||||
void onPKMuteClick(int position, boolean mute);
|
||||
|
||||
void onEmptyView(int position, boolean empty);
|
||||
}
|
||||
|
||||
public interface OnPKItemViewChangedListener {
|
||||
void onItemViewAttachedToWindow(int position);
|
||||
|
||||
void onItemViewDetachedToWindow(int position);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,470 @@
|
||||
package com.alivc.live.interactive_pk;
|
||||
|
||||
import static com.alivc.live.interactive_common.utils.LivePushGlobalConfig.mAlivcLivePushConfig;
|
||||
|
||||
import android.content.Context;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import com.alivc.live.annotations.AlivcLiveMuteLocalAudioMode;
|
||||
import com.alivc.live.commonbiz.LocalStreamReader;
|
||||
import com.alivc.live.commonbiz.ResourcesConst;
|
||||
import com.alivc.live.commonbiz.test.AliLiveStreamURLUtil;
|
||||
import com.alivc.live.interactive_common.InteractiveMode;
|
||||
import com.alivc.live.interactive_common.bean.InteractiveUserData;
|
||||
import com.alivc.live.interactive_common.listener.InteractLivePushPullListener;
|
||||
import com.alivc.live.interactive_common.utils.LivePushGlobalConfig;
|
||||
import com.alivc.live.player.annotations.AlivcLivePlayVideoStreamType;
|
||||
import com.alivc.live.pusher.AlivcImageFormat;
|
||||
import com.alivc.live.pusher.AlivcLiveLocalRecordConfig;
|
||||
import com.alivc.live.pusher.AlivcLivePushAudioEffectVoiceChangeMode;
|
||||
import com.alivc.live.pusher.AlivcLivePushAudioFrame;
|
||||
import com.alivc.live.pusher.AlivcLivePushExternalAudioStreamConfig;
|
||||
import com.alivc.live.pusher.AlivcLivePushVideoConfig;
|
||||
import com.alivc.live.pusher.AlivcLivePusherRawDataSample;
|
||||
import com.alivc.live.pusher.AlivcPreviewDisplayMode;
|
||||
import com.alivc.live.pusher.AlivcResolutionEnum;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class PKController {
|
||||
|
||||
private final PKLiveManager mPKLiveManager;
|
||||
private final Context mContext;
|
||||
|
||||
private int mAudioStreamId = -1;
|
||||
|
||||
// 主播信息
|
||||
public InteractiveUserData mOwnerUserData;
|
||||
// PK主播信息
|
||||
public InteractiveUserData mOtherUserData;
|
||||
|
||||
// 外部音视频输入
|
||||
private LocalStreamReader mLocalStreamReader;
|
||||
private LocalStreamReader mLocalStreamReader2;
|
||||
private LocalStreamReader mLocalStreamReader3;
|
||||
|
||||
public PKController(Context context, InteractiveUserData userData) {
|
||||
mPKLiveManager = new PKLiveManager();
|
||||
mPKLiveManager.init(context.getApplicationContext(), InteractiveMode.PK);
|
||||
|
||||
this.mContext = context;
|
||||
initExternalStreamAV();
|
||||
mOwnerUserData = userData;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 PK 主播的信息
|
||||
*/
|
||||
public void setPKOtherInfo(InteractiveUserData userData) {
|
||||
if (userData == null) {
|
||||
return;
|
||||
}
|
||||
mOtherUserData = userData;
|
||||
mOtherUserData.url = AliLiveStreamURLUtil.generateInteractivePullUrl(userData.channelId, userData.userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始推流
|
||||
*
|
||||
* @param frameLayout 推流画面渲染 View 的 parent ViewGroup
|
||||
*/
|
||||
public void startPush(FrameLayout frameLayout) {
|
||||
externAV();
|
||||
mPKLiveManager.startPreviewAndPush(mOwnerUserData, frameLayout, true);
|
||||
}
|
||||
|
||||
private void initExternalStreamAV() {
|
||||
AlivcResolutionEnum externalStreamResolution = AlivcResolutionEnum.RESOLUTION_720P;
|
||||
int externalStreamWidth = AlivcResolutionEnum.getResolutionWidth(externalStreamResolution, LivePushGlobalConfig.mAlivcLivePushConfig.getLivePushMode());
|
||||
int externalStreamHeight = AlivcResolutionEnum.getResolutionHeight(externalStreamResolution, LivePushGlobalConfig.mAlivcLivePushConfig.getLivePushMode());
|
||||
mLocalStreamReader = new LocalStreamReader.Builder()
|
||||
.setVideoWith(externalStreamWidth)
|
||||
.setVideoHeight(externalStreamHeight)
|
||||
.setVideoStride(externalStreamWidth)
|
||||
.setVideoSize(externalStreamHeight * externalStreamWidth * 3 / 2)
|
||||
.setVideoRotation(0)
|
||||
.setAudioSampleRate(44100)
|
||||
.setAudioChannel(1)
|
||||
.setAudioBufferSize(2048)
|
||||
.build();
|
||||
mLocalStreamReader2 = new LocalStreamReader.Builder()
|
||||
.setVideoWith(externalStreamWidth)
|
||||
.setVideoHeight(externalStreamHeight)
|
||||
.setVideoStride(externalStreamWidth)
|
||||
.setVideoSize(externalStreamHeight * externalStreamWidth * 3 / 2)
|
||||
.setVideoRotation(0)
|
||||
.setAudioSampleRate(44100)
|
||||
.setAudioChannel(1)
|
||||
.setAudioBufferSize(2048)
|
||||
.build();
|
||||
mLocalStreamReader3 = new LocalStreamReader.Builder()
|
||||
.setVideoWith(externalStreamWidth)
|
||||
.setVideoHeight(externalStreamHeight)
|
||||
.setVideoStride(externalStreamWidth)
|
||||
.setVideoSize(externalStreamHeight * externalStreamWidth * 3 / 2)
|
||||
.setVideoRotation(0)
|
||||
.setAudioSampleRate(44100)
|
||||
.setAudioChannel(1)
|
||||
.setAudioBufferSize(2048)
|
||||
.build();
|
||||
}
|
||||
|
||||
private void externAV() {
|
||||
if (LivePushGlobalConfig.mAlivcLivePushConfig.isExternMainStream()) {
|
||||
File yuvFile = ResourcesConst.localCaptureYUVFilePath(mContext);
|
||||
mLocalStreamReader.readYUVData(yuvFile, (buffer, pts, videoWidth, videoHeight, videoStride, videoSize, videoRotation) -> {
|
||||
mPKLiveManager.inputStreamVideoData(buffer, videoWidth, videoHeight, videoStride, videoSize, pts, videoRotation);
|
||||
});
|
||||
File pcmFile = ResourcesConst.localCapturePCMFilePath(mContext);
|
||||
mLocalStreamReader.readPCMData(pcmFile, (buffer, length, pts, audioSampleRate, audioChannel) -> {
|
||||
mPKLiveManager.inputStreamAudioData(buffer, length, audioSampleRate, audioChannel, pts);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void startExternalVideoStream() {
|
||||
// 启用外部视频输入源
|
||||
mAlivcLivePushConfig.setAlivcExternMainImageFormat(AlivcImageFormat.IMAGE_FORMAT_YUVNV12);
|
||||
mPKLiveManager.setExternalVideoSource(true, false, AlivcLivePlayVideoStreamType.STREAM_CAMERA,
|
||||
AlivcPreviewDisplayMode.ALIVC_LIVE_PUSHER_PREVIEW_ASPECT_FILL);
|
||||
File yuvFile = ResourcesConst.localCaptureYUVFilePath(mContext);
|
||||
mLocalStreamReader2.readYUVData(yuvFile, (buffer, pts, videoWidth, videoHeight, videoStride, videoSize, videoRotation) -> {
|
||||
mPKLiveManager.inputStreamVideoData(buffer, videoWidth, videoHeight, videoStride, videoSize, pts, videoRotation);
|
||||
});
|
||||
}
|
||||
|
||||
public void stopExternalVideoStream() {
|
||||
mPKLiveManager.setExternalVideoSource(false, false, AlivcLivePlayVideoStreamType.STREAM_CAMERA,
|
||||
AlivcPreviewDisplayMode.ALIVC_LIVE_PUSHER_PREVIEW_ASPECT_FILL);
|
||||
mLocalStreamReader2.stopYUV();
|
||||
}
|
||||
|
||||
public void startExternalAudioStream() {
|
||||
// 新增外部音频流
|
||||
AlivcLivePushExternalAudioStreamConfig externalAudioStreamConfig = new AlivcLivePushExternalAudioStreamConfig();
|
||||
externalAudioStreamConfig.channels = 1;
|
||||
externalAudioStreamConfig.sampleRate = 44100;
|
||||
externalAudioStreamConfig.playoutVolume = 50;
|
||||
externalAudioStreamConfig.publishVolume = 50;
|
||||
externalAudioStreamConfig.publishStream = 0; // 指定绑定的音频流 1-第二音频流,0-MIC流
|
||||
|
||||
mAudioStreamId = mPKLiveManager.addExternalAudioStream(externalAudioStreamConfig);
|
||||
|
||||
// 设置是否与麦克风采集音频混合
|
||||
mPKLiveManager.setMixedWithMic(true);
|
||||
|
||||
// 设置外部音频流播放音量
|
||||
mPKLiveManager.setExternalAudioStreamPlayoutVolume(mAudioStreamId, 30);
|
||||
// 设置外部音频流推流音量
|
||||
mPKLiveManager.setExternalAudioStreamPublishVolume(mAudioStreamId, 100);
|
||||
|
||||
// 输入外部音频流数据
|
||||
File pcmFile = ResourcesConst.localCapturePCMFilePath(mContext);
|
||||
mLocalStreamReader2.readPCMData(pcmFile, (buffer, length, pts, audioSampleRate, audioChannel) -> {
|
||||
AlivcLivePushAudioFrame audioFrame = new AlivcLivePushAudioFrame();
|
||||
audioFrame.data = buffer;
|
||||
audioFrame.numSamples = length / 8;
|
||||
audioFrame.bytesPerSample = 8;
|
||||
audioFrame.numChannels = audioChannel;
|
||||
audioFrame.samplesPerSec = audioSampleRate;
|
||||
mPKLiveManager.pushExternalAudioStream(mAudioStreamId, audioFrame);
|
||||
});
|
||||
}
|
||||
|
||||
public void stopExternalAudioStream() {
|
||||
// 删除外部音频流
|
||||
mPKLiveManager.removeExternalAudioStream(mAudioStreamId);
|
||||
mLocalStreamReader2.stopPcm();
|
||||
}
|
||||
|
||||
// 开启第二路音频流推送
|
||||
public void startDualAudioStream() {
|
||||
// 开启后第二路音频流推送
|
||||
mPKLiveManager.startDualAudioStream();
|
||||
|
||||
// 新增外部音频流,用于第二路音频流推送,注意 publishStream 必须为1
|
||||
AlivcLivePushExternalAudioStreamConfig dualAudioStreamConfig = new AlivcLivePushExternalAudioStreamConfig();
|
||||
dualAudioStreamConfig.channels = 1;
|
||||
dualAudioStreamConfig.sampleRate = 44100;
|
||||
dualAudioStreamConfig.playoutVolume = 50;
|
||||
dualAudioStreamConfig.publishVolume = 50;
|
||||
dualAudioStreamConfig.publishStream = 1; // 指定绑定的音频流 1-第二音频流,0-MIC流
|
||||
|
||||
// 添加音频数据源
|
||||
mAudioStreamId = mPKLiveManager.addExternalAudioStream(dualAudioStreamConfig);
|
||||
|
||||
// 输入外部音频流数据
|
||||
File pcmFile = ResourcesConst.localCapturePCMFilePath(mContext);
|
||||
mLocalStreamReader3.readPCMData(pcmFile, (buffer, length, pts, audioSampleRate, audioChannel) -> {
|
||||
AlivcLivePushAudioFrame audioFrame = new AlivcLivePushAudioFrame();
|
||||
audioFrame.data = buffer;
|
||||
audioFrame.numSamples = length / 8;
|
||||
audioFrame.bytesPerSample = 8;
|
||||
audioFrame.numChannels = audioChannel;
|
||||
audioFrame.samplesPerSec = audioSampleRate;
|
||||
// 向第二路音频通道输入音频数据
|
||||
mPKLiveManager.pushExternalAudioStream(mAudioStreamId, audioFrame);
|
||||
});
|
||||
}
|
||||
|
||||
// 停止第二路音频流推送
|
||||
public void stopDualAudioStream() {
|
||||
// 停止第二路音频流推送
|
||||
mPKLiveManager.stopDualAudioStream();
|
||||
|
||||
// 删除外部音频流
|
||||
mPKLiveManager.removeExternalAudioStream(mAudioStreamId);
|
||||
|
||||
mLocalStreamReader3.stopPcm();
|
||||
}
|
||||
|
||||
// 开始屏幕共享流
|
||||
public void startScreenShareStream() {
|
||||
mPKLiveManager.startScreenShare();
|
||||
|
||||
mPKLiveManager.setExternalVideoSource(true, false, AlivcLivePlayVideoStreamType.STREAM_SCREEN,
|
||||
AlivcPreviewDisplayMode.ALIVC_LIVE_PUSHER_PREVIEW_ASPECT_FIT);
|
||||
|
||||
File yuvFile = ResourcesConst.localCaptureYUVFilePath(mContext);
|
||||
mLocalStreamReader3.readYUVData(yuvFile, (buffer, pts, videoWidth, videoHeight, videoStride, videoSize, videoRotation) -> {
|
||||
AlivcLivePusherRawDataSample rawDataSample = new AlivcLivePusherRawDataSample();
|
||||
rawDataSample.format = AlivcImageFormat.IMAGE_FORMAT_YUVNV12;
|
||||
rawDataSample.width = videoWidth;
|
||||
rawDataSample.height = videoHeight;
|
||||
rawDataSample.frame = buffer;
|
||||
rawDataSample.videoFrameLength = videoSize;
|
||||
rawDataSample.timestamp = pts;
|
||||
mPKLiveManager.pushExternalVideoFrame(rawDataSample, AlivcLivePlayVideoStreamType.STREAM_SCREEN);
|
||||
});
|
||||
}
|
||||
|
||||
// 停止屏幕共享流
|
||||
public void stopScreenShareStream() {
|
||||
mPKLiveManager.stopScreenShare();
|
||||
|
||||
mPKLiveManager.setExternalVideoSource(false, false, AlivcLivePlayVideoStreamType.STREAM_SCREEN,
|
||||
AlivcPreviewDisplayMode.ALIVC_LIVE_PUSHER_PREVIEW_ASPECT_FIT);
|
||||
|
||||
mLocalStreamReader3.stopYUV();
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始 PK (拉流)
|
||||
*/
|
||||
public void startPK(InteractiveUserData userData, FrameLayout frameLayout) {
|
||||
mPKLiveManager.setPullView(userData, frameLayout, true);
|
||||
mPKLiveManager.startPullRTCStream(mOtherUserData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止 PK (拉流)
|
||||
*/
|
||||
public void stopPK() {
|
||||
mPKLiveManager.stopPullRTCStream(mOtherUserData);
|
||||
}
|
||||
|
||||
public void setPKLiveMixTranscoding(boolean needMix) {
|
||||
if (needMix) {
|
||||
mPKLiveManager.setLiveMixTranscodingConfig(mOwnerUserData, mOtherUserData, false);
|
||||
} else {
|
||||
mPKLiveManager.clearLiveMixTranscodingConfig();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 静音对端主播(一般用于PK惩罚环节的业务场景)
|
||||
*
|
||||
* @param userKey userKey
|
||||
* @param mute 是否静音
|
||||
* @see <a href="https://help.aliyun.com/zh/live/user-guide/configure-mute-in-battle-scenarios">主播PK场景实现静音功能</a>
|
||||
*/
|
||||
public void mutePKAnchor(String userKey, boolean mute) {
|
||||
if (!isPKing() || mPKLiveManager == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 更新连麦静音
|
||||
if (mute) {
|
||||
mPKLiveManager.pauseAudioPlaying(userKey);
|
||||
} else {
|
||||
mPKLiveManager.resumeAudioPlaying(userKey);
|
||||
}
|
||||
|
||||
// 更新混流静音
|
||||
mPKLiveManager.muteAnchorMultiStream(mPKLiveManager.getUserDataByKey(userKey), mute);
|
||||
}
|
||||
|
||||
public boolean isPKing() {
|
||||
return mPKLiveManager.isPulling(mOtherUserData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始 PK (多人 PK 场景)
|
||||
*/
|
||||
public void startMultiPK(InteractiveUserData userData, FrameLayout frameLayout) {
|
||||
if (userData == null) {
|
||||
return;
|
||||
}
|
||||
mPKLiveManager.setPullView(userData, frameLayout, true);
|
||||
mPKLiveManager.startPullRTCStream(userData);
|
||||
}
|
||||
|
||||
public void setPullView(InteractiveUserData userData, FrameLayout frameLayout) {
|
||||
mPKLiveManager.setPullView(userData, frameLayout, true);
|
||||
}
|
||||
|
||||
public void updatePreviewView(FrameLayout frameLayout, boolean isFullScreen) {
|
||||
mPKLiveManager.updatePreview(frameLayout, isFullScreen);
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止 PK (多人 PK 场景)
|
||||
*/
|
||||
public void stopMultiPK(InteractiveUserData userData) {
|
||||
mPKLiveManager.stopPullRTCStream(userData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 恢复视频流
|
||||
*
|
||||
* @param userKey userKey
|
||||
*/
|
||||
public void resumeVideoPlaying(String userKey) {
|
||||
mPKLiveManager.resumePlayRTCStream(mPKLiveManager.getUserDataByKey(userKey));
|
||||
}
|
||||
|
||||
/**
|
||||
* 暂停视频流
|
||||
*
|
||||
* @param userKey userKey
|
||||
*/
|
||||
public void pauseVideoPlaying(String userKey) {
|
||||
mPKLiveManager.pausePlayRTCStream(mPKLiveManager.getUserDataByKey(userKey));
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 PK 主播的信息(多人PK场景)
|
||||
*/
|
||||
public void setMultiPKOtherInfo(InteractiveUserData userData) {
|
||||
setPKOtherInfo(userData);
|
||||
}
|
||||
|
||||
public boolean isPKing(InteractiveUserData userData) {
|
||||
return mPKLiveManager.isPulling(userData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置混流 (多人 PK 场景)
|
||||
*
|
||||
* @param isOwnerUserId 是否是主播自己的 id
|
||||
* @param userData 主播 userData (如果 isOwnerUserId 为 true,则 userId 表示主播自己的 id,否则表示 pk 方的主播 id)
|
||||
* @param frameLayout 当 isOwnerUserId 为 false 时,需要设置 pk 方主播的混流,需要传递 frameLayout 计算混流位置
|
||||
*/
|
||||
public void addMultiPKLiveMixTranscoding(boolean isOwnerUserId, InteractiveUserData userData, FrameLayout frameLayout) {
|
||||
if (isOwnerUserId) {
|
||||
mPKLiveManager.addAnchorMixTranscodingConfig(userData);
|
||||
} else {
|
||||
mPKLiveManager.addAudienceMixTranscodingConfig(userData, frameLayout);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置混流 (多人 PK 场景)
|
||||
* 当 isOwnerUserId 为 true 时,表示主播自己退出 PK,则删除所有混流设置。否则只删除 PK 方的混流。
|
||||
*
|
||||
* @param isOwnerUserId 是否是主播自己的 id
|
||||
* @param userData 主播 userData (如果 isOwnerUserId 为 true,则 userId 表示主播自己的 id,否则表示 pk 方的主播 id)
|
||||
*/
|
||||
public void removeMultiPKLiveMixTranscoding(boolean isOwnerUserId, InteractiveUserData userData) {
|
||||
if (isOwnerUserId) {
|
||||
mPKLiveManager.clearLiveMixTranscodingConfig();
|
||||
} else {
|
||||
mPKLiveManager.removeLiveMixTranscodingConfig(userData);
|
||||
}
|
||||
}
|
||||
|
||||
public void setPKLivePushPullListener(InteractLivePushPullListener listener) {
|
||||
mPKLiveManager.setInteractLivePushPullListener(listener);
|
||||
}
|
||||
|
||||
public void setMultiPKLivePushPullListener(InteractLivePushPullListener listener) {
|
||||
mPKLiveManager.setInteractLivePushPullListener(listener);
|
||||
}
|
||||
|
||||
public void release() {
|
||||
mLocalStreamReader.stopYUV();
|
||||
mLocalStreamReader.stopPcm();
|
||||
mLocalStreamReader2.stopYUV();
|
||||
mLocalStreamReader2.stopPcm();
|
||||
mLocalStreamReader3.stopYUV();
|
||||
mLocalStreamReader3.stopPcm();
|
||||
stopExternalAudioStream();
|
||||
stopExternalVideoStream();
|
||||
stopDualAudioStream();
|
||||
mPKLiveManager.release();
|
||||
}
|
||||
|
||||
public void setBGMEarsBack(boolean isOpen) {
|
||||
mPKLiveManager.setBGMEarsBack(isOpen);
|
||||
}
|
||||
|
||||
public void setAudioEffectVoiceChangeMode(AlivcLivePushAudioEffectVoiceChangeMode voiceChangeMode) {
|
||||
mPKLiveManager.setAudioEffectVoiceChangeMode(voiceChangeMode);
|
||||
}
|
||||
|
||||
public void playAudioEffects() {
|
||||
mPKLiveManager.playAudioEffects();
|
||||
}
|
||||
|
||||
public void stopAudioEffect() {
|
||||
mPKLiveManager.stopAudioEffect();
|
||||
}
|
||||
|
||||
public void setVideoConfig(AlivcLivePushVideoConfig videoConfig) {
|
||||
mPKLiveManager.setVideoConfig(videoConfig);
|
||||
}
|
||||
|
||||
public void switchCamera() {
|
||||
mPKLiveManager.switchCamera();
|
||||
}
|
||||
|
||||
public void enableSpeakerPhone(boolean enable) {
|
||||
mPKLiveManager.enableSpeakerPhone(enable);
|
||||
}
|
||||
|
||||
public void setMute(boolean b) {
|
||||
mPKLiveManager.setMute(b);
|
||||
}
|
||||
|
||||
public void setMute(boolean isMute, AlivcLiveMuteLocalAudioMode muteLocalAudioMode) {
|
||||
mPKLiveManager.setMute(isMute, muteLocalAudioMode);
|
||||
}
|
||||
|
||||
public void enableAudioCapture(boolean enable) {
|
||||
mPKLiveManager.enableAudioCapture(enable);
|
||||
}
|
||||
|
||||
public void muteLocalCamera(boolean muteLocalCamera) {
|
||||
mPKLiveManager.muteLocalCamera(muteLocalCamera);
|
||||
}
|
||||
|
||||
public void enableLocalCamera(boolean enable) {
|
||||
mPKLiveManager.enableLocalCamera(enable);
|
||||
}
|
||||
|
||||
public void sendSEI(String text, int payload) {
|
||||
mPKLiveManager.sendCustomMessage(text, payload);
|
||||
}
|
||||
|
||||
public void startLocalRecord(AlivcLiveLocalRecordConfig recordConfig) {
|
||||
if (mPKLiveManager != null) {
|
||||
mPKLiveManager.startLocalRecord(recordConfig);
|
||||
}
|
||||
}
|
||||
|
||||
public void stopLocalRecord() {
|
||||
if (mPKLiveManager != null) {
|
||||
mPKLiveManager.stopLocalRecord();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,519 @@
|
||||
package com.alivc.live.interactive_pk;
|
||||
|
||||
import android.graphics.Color;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import com.alibaba.android.arouter.facade.annotation.Route;
|
||||
import com.alivc.live.annotations.AlivcLiveMuteLocalAudioMode;
|
||||
import com.alivc.live.annotations.AlivcLiveRecordAudioQuality;
|
||||
import com.alivc.live.annotations.AlivcLiveRecordMediaFormat;
|
||||
import com.alivc.live.annotations.AlivcLiveRecordStreamType;
|
||||
import com.alivc.live.commonbiz.ResourcesConst;
|
||||
import com.alivc.live.commonui.avdialog.AUILiveDialog;
|
||||
import com.alivc.live.commonui.messageview.AutoScrollMessagesView;
|
||||
import com.alivc.live.commonui.seiview.LivePusherSEIView;
|
||||
import com.alivc.live.commonui.utils.StatusBarUtil;
|
||||
import com.alivc.live.commonui.widgets.LivePushTextSwitch;
|
||||
import com.alivc.live.commonui.widgets.ResizableFrameLayout;
|
||||
import com.alivc.live.commonutils.FileUtil;
|
||||
import com.alivc.live.commonutils.ToastUtils;
|
||||
import com.alivc.live.interactive_common.InteractiveConstants;
|
||||
import com.alivc.live.interactive_common.bean.InteractiveUserData;
|
||||
import com.alivc.live.interactive_common.listener.ConnectionLostListener;
|
||||
import com.alivc.live.interactive_common.listener.InteractLivePushPullListener;
|
||||
import com.alivc.live.interactive_common.listener.InteractLiveTipsViewListener;
|
||||
import com.alivc.live.interactive_common.utils.InteractLiveIntent;
|
||||
import com.alivc.live.interactive_common.utils.LivePushGlobalConfig;
|
||||
import com.alivc.live.interactive_common.widget.ConnectionLostTipsView;
|
||||
import com.alivc.live.interactive_common.widget.InteractiveCommonInputView;
|
||||
import com.alivc.live.interactive_common.widget.InteractivePaneControlView;
|
||||
import com.alivc.live.interactive_common.widget.InteractiveRoomControlView;
|
||||
import com.alivc.live.interactive_common.widget.RoomAndUserInfoView;
|
||||
import com.alivc.live.player.annotations.AlivcLivePlayError;
|
||||
import com.alivc.live.pusher.AlivcLiveLocalRecordConfig;
|
||||
import com.alivc.live.pusher.AlivcLivePushAudioEffectVoiceChangeMode;
|
||||
import com.alivc.live.pusher.AlivcLivePushVideoConfig;
|
||||
import com.alivc.live.pusher.AlivcResolutionEnum;
|
||||
import com.alivc.live.pusher.AlivcVideoEncodeGopEnum;
|
||||
import com.aliyunsdk.queen.menu.QueenBeautyMenu;
|
||||
import com.aliyunsdk.queen.menu.QueenMenuPanel;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* PK 互动界面
|
||||
*/
|
||||
@Route(path = "/interactivePK/pkLive")
|
||||
public class PKLiveActivity extends AppCompatActivity {
|
||||
|
||||
private PKController mPKController;
|
||||
private AUILiveDialog mAUILiveDialog;
|
||||
private InteractLiveIntent mCurrentIntent;
|
||||
private ImageView mCloseImageView;
|
||||
private TextView mConnectTextView;
|
||||
private TextView mShowConnectTextView;
|
||||
private ResizableFrameLayout mOwnerFrameLayout;
|
||||
private ResizableFrameLayout mOtherFrameLayout;
|
||||
private FrameLayout mUnConnectFrameLayout;
|
||||
private ConnectionLostTipsView mConnectionLostTipsView;
|
||||
private RoomAndUserInfoView mOwnerInfoView;
|
||||
private InteractivePaneControlView mOwnerCtrlView;
|
||||
private RoomAndUserInfoView mOtherInfoView;
|
||||
private InteractivePaneControlView mOtherCtrlView;
|
||||
private InteractiveRoomControlView mInteractiveRoomControlView;
|
||||
|
||||
private AutoScrollMessagesView mSeiMessageView;
|
||||
private ImageView mBeautyImageView;
|
||||
// 美颜menu
|
||||
private QueenBeautyMenu mQueenBeautyMenu;
|
||||
private QueenMenuPanel mBeautyMenuPanel;
|
||||
|
||||
private TextView mHomeIdTextView;
|
||||
|
||||
private LivePusherSEIView mSeiView;
|
||||
|
||||
private InteractiveUserData mOtherUserData;
|
||||
|
||||
private LivePushTextSwitch mShowCustomMessageView;
|
||||
private LivePushTextSwitch mLocalRecordView;
|
||||
private LivePushTextSwitch mExternalAudioStreamView;
|
||||
private LivePushTextSwitch mExternalVideoStreamView;
|
||||
private LivePushTextSwitch mPushDualAudioView;
|
||||
private LivePushTextSwitch mPushScreenShareView;
|
||||
private LivePushTextSwitch mChangeResolutionFPSView;
|
||||
private LivePushTextSwitch mForceEarMonitorView;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
StatusBarUtil.translucent(this, Color.TRANSPARENT);
|
||||
|
||||
setContentView(R.layout.activity_pklive);
|
||||
|
||||
initView();
|
||||
initListener();
|
||||
initData();
|
||||
}
|
||||
|
||||
private void initView() {
|
||||
mAUILiveDialog = new AUILiveDialog(this);
|
||||
mCloseImageView = findViewById(R.id.iv_close);
|
||||
mConnectTextView = findViewById(R.id.tv_connect);
|
||||
mOwnerFrameLayout = findViewById(R.id.frame_owner);
|
||||
mOtherFrameLayout = findViewById(R.id.render_container);
|
||||
mUnConnectFrameLayout = findViewById(R.id.fl_un_connect);
|
||||
mShowConnectTextView = findViewById(R.id.tv_show_connect);
|
||||
mSeiView = findViewById(R.id.sei_view);
|
||||
mSeiMessageView = findViewById(R.id.sei_receive_view);
|
||||
|
||||
mBeautyImageView = findViewById(R.id.iv_beauty);
|
||||
|
||||
mBeautyMenuPanel = QueenBeautyMenu.getPanel(this);
|
||||
mBeautyMenuPanel.onHideMenu();
|
||||
mBeautyMenuPanel.onHideValidFeatures();
|
||||
mBeautyMenuPanel.onHideCopyright();
|
||||
|
||||
mQueenBeautyMenu = findViewById(R.id.beauty_beauty_menuPanel);
|
||||
mQueenBeautyMenu.addView(mBeautyMenuPanel);
|
||||
|
||||
mHomeIdTextView = findViewById(R.id.tv_home_id);
|
||||
|
||||
mOwnerInfoView = findViewById(R.id.owner_info_view);
|
||||
mOwnerCtrlView = findViewById(R.id.owner_ctrl_view);
|
||||
mOwnerCtrlView.setOnClickEventListener(new InteractivePaneControlView.OnClickEventListener() {
|
||||
@Override
|
||||
public void onClickMuteAudio(boolean mute) {
|
||||
mPKController.setMute(mute);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClickEmptyView(boolean empty) {
|
||||
mPKController.updatePreviewView(empty ? null : mOwnerFrameLayout, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClickResize(boolean resize) {
|
||||
mOwnerFrameLayout.resize();
|
||||
}
|
||||
});
|
||||
|
||||
mOtherInfoView = findViewById(R.id.other_info_view);
|
||||
|
||||
mOtherCtrlView = findViewById(R.id.other_ctrl_view);
|
||||
mOtherCtrlView.enableMute(true);
|
||||
mOtherCtrlView.setOnClickEventListener(new InteractivePaneControlView.OnClickEventListener() {
|
||||
@Override
|
||||
public void onClickMuteAudio(boolean mute) {
|
||||
mPKController.mutePKAnchor(mOtherUserData.getKey(), mute);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClickEmptyView(boolean empty) {
|
||||
mPKController.setPullView(mOtherUserData, empty ? null : mOtherFrameLayout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClickResize(boolean resize) {
|
||||
mOtherFrameLayout.resize();
|
||||
}
|
||||
});
|
||||
|
||||
mInteractiveRoomControlView = findViewById(R.id.interactive_setting_view);
|
||||
|
||||
mConnectionLostTipsView = new ConnectionLostTipsView(this);
|
||||
|
||||
mShowCustomMessageView = findViewById(R.id.btn_show_custom_message);
|
||||
mShowCustomMessageView.setTextViewText(getString(R.string.sei_send_custom_message_tv));
|
||||
mShowCustomMessageView.setOnSwitchToggleListener(isChecked -> {
|
||||
int visibility = isChecked ? View.VISIBLE : View.GONE;
|
||||
mSeiView.setVisibility(visibility);
|
||||
});
|
||||
|
||||
// 本地录制
|
||||
mLocalRecordView = findViewById(R.id.btn_local_record);
|
||||
mLocalRecordView.setTextViewText(getString(R.string.local_record_tv));
|
||||
mLocalRecordView.setOnSwitchToggleListener(isChecked -> {
|
||||
if (mPKController == null) {
|
||||
return;
|
||||
}
|
||||
if (isChecked) {
|
||||
AlivcLiveLocalRecordConfig recordConfig = new AlivcLiveLocalRecordConfig();
|
||||
String dateFormat = new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date());
|
||||
recordConfig.storagePath = FileUtil.getExternalCacheFolder(getBaseContext()) + "/" + dateFormat + ".mp4";
|
||||
recordConfig.streamType = AlivcLiveRecordStreamType.AUDIO_VIDEO;
|
||||
recordConfig.audioQuality = AlivcLiveRecordAudioQuality.MEDIUM;
|
||||
recordConfig.mediaFormat = AlivcLiveRecordMediaFormat.MP4;
|
||||
mPKController.startLocalRecord(recordConfig);
|
||||
} else {
|
||||
mPKController.stopLocalRecord();
|
||||
}
|
||||
});
|
||||
|
||||
mExternalAudioStreamView = findViewById(R.id.btn_external_audio_stream);
|
||||
mExternalAudioStreamView.setTextViewText(getString(R.string.external_audio_stream_tv));
|
||||
mExternalAudioStreamView.setOnSwitchToggleListener(isChecked -> {
|
||||
// 输入外部音频流数据
|
||||
if (!checkMediaFileExists()) {
|
||||
return;
|
||||
}
|
||||
if (isChecked) {
|
||||
mPKController.startExternalAudioStream();
|
||||
} else {
|
||||
mPKController.stopExternalAudioStream();
|
||||
}
|
||||
});
|
||||
|
||||
mExternalVideoStreamView = findViewById(R.id.btn_external_video_stream);
|
||||
mExternalVideoStreamView.setTextViewText(getString(R.string.external_video_stream_tv));
|
||||
mExternalVideoStreamView.setOnSwitchToggleListener(isChecked -> {
|
||||
// 输入外部视频流数据
|
||||
if (!checkMediaFileExists()) {
|
||||
return;
|
||||
}
|
||||
if (isChecked) {
|
||||
mPKController.startExternalVideoStream();
|
||||
} else {
|
||||
mPKController.stopExternalVideoStream();
|
||||
}
|
||||
});
|
||||
|
||||
mPushDualAudioView = findViewById(R.id.btn_push_dual_audio);
|
||||
mPushDualAudioView.setTextViewText(getString(R.string.dual_audio_stream));
|
||||
mPushDualAudioView.setOnSwitchToggleListener(isChecked -> {
|
||||
if (!checkMediaFileExists()) {
|
||||
return;
|
||||
}
|
||||
if (isChecked) {
|
||||
mPKController.startDualAudioStream();
|
||||
} else {
|
||||
mPKController.stopDualAudioStream();
|
||||
}
|
||||
});
|
||||
|
||||
mPushScreenShareView = findViewById(R.id.btn_push_screen_share);
|
||||
mPushScreenShareView.setTextViewText(getString(R.string.screen_share_stream));
|
||||
mPushScreenShareView.setOnSwitchToggleListener(isChecked -> {
|
||||
if (!checkMediaFileExists()) {
|
||||
return;
|
||||
}
|
||||
if (isChecked) {
|
||||
mPKController.startScreenShareStream();
|
||||
} else {
|
||||
mPKController.stopScreenShareStream();
|
||||
}
|
||||
});
|
||||
|
||||
mChangeResolutionFPSView = findViewById(R.id.btn_change_resolution_and_fps);
|
||||
mChangeResolutionFPSView.setTextViewText(getString(R.string.change_resolution_and_fps));
|
||||
mChangeResolutionFPSView.setOnSwitchToggleListener(isChecked -> {
|
||||
if (isChecked) {
|
||||
AlivcLivePushVideoConfig videoConfig = new AlivcLivePushVideoConfig();
|
||||
videoConfig.resolution = AlivcResolutionEnum.RESOLUTION_540P;
|
||||
videoConfig.initialBitrate = 1200;
|
||||
videoConfig.targetBitrate = 1200;
|
||||
videoConfig.minBitrate = 300;
|
||||
videoConfig.fps = 15;
|
||||
videoConfig.gop = AlivcVideoEncodeGopEnum.GOP_TWO;
|
||||
mPKController.setVideoConfig(videoConfig);
|
||||
} else {
|
||||
AlivcLivePushVideoConfig videoConfig = new AlivcLivePushVideoConfig();
|
||||
videoConfig.resolution = AlivcResolutionEnum.RESOLUTION_720P;
|
||||
videoConfig.initialBitrate = 1800;
|
||||
videoConfig.targetBitrate = 1800;
|
||||
videoConfig.minBitrate = 300;
|
||||
videoConfig.fps = 20;
|
||||
videoConfig.gop = AlivcVideoEncodeGopEnum.GOP_TWO;
|
||||
mPKController.setVideoConfig(videoConfig);
|
||||
}
|
||||
});
|
||||
|
||||
mForceEarMonitorView = findViewById(R.id.btn_force_ear_monitor);
|
||||
mForceEarMonitorView.setTextViewText(getString(R.string.force_ear_monitor));
|
||||
mForceEarMonitorView.setOnSwitchToggleListener(isChecked -> {
|
||||
if (isChecked) {
|
||||
mPKController.setMute(true, AlivcLiveMuteLocalAudioMode.MUTE_ONLY_MIC);
|
||||
mPKController.setAudioEffectVoiceChangeMode(AlivcLivePushAudioEffectVoiceChangeMode.SOUND_EFFECT_BABY_BOY);
|
||||
mPKController.setBGMEarsBack(true);
|
||||
} else {
|
||||
mPKController.setMute(false, AlivcLiveMuteLocalAudioMode.MUTE_ONLY_MIC);
|
||||
mPKController.setAudioEffectVoiceChangeMode(AlivcLivePushAudioEffectVoiceChangeMode.SOUND_EFFECT_OFF);
|
||||
mPKController.setBGMEarsBack(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void initListener() {
|
||||
//美颜
|
||||
mBeautyImageView.setOnClickListener(view -> {
|
||||
if (LivePushGlobalConfig.ENABLE_BEAUTY) {
|
||||
if (mQueenBeautyMenu.getVisibility() == View.VISIBLE) {
|
||||
mQueenBeautyMenu.setVisibility(View.GONE);
|
||||
mBeautyMenuPanel.onHideMenu();
|
||||
} else {
|
||||
mQueenBeautyMenu.setVisibility(View.VISIBLE);
|
||||
mBeautyMenuPanel.onShowMenu();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
mSeiView.setSendSeiViewListener((payload, text) -> mPKController.sendSEI(text, payload));
|
||||
mConnectionLostTipsView.setConnectionLostListener(new ConnectionLostListener() {
|
||||
@Override
|
||||
public void onConfirm() {
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
finish();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
mInteractiveRoomControlView.setOnClickEventListener(new InteractiveRoomControlView.OnClickEventListener() {
|
||||
@Override
|
||||
public void onClickSwitchCamera() {
|
||||
mPKController.switchCamera();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClickSpeakerPhone(boolean enable) {
|
||||
mPKController.enableSpeakerPhone(enable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClickMuteAudio(boolean mute) {
|
||||
mPKController.setMute(mute);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClickEnableAudio(boolean enable) {
|
||||
mPKController.enableAudioCapture(enable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClickMuteVideo(boolean mute) {
|
||||
mPKController.muteLocalCamera(mute);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClickEnableVideo(boolean enable) {
|
||||
mPKController.enableLocalCamera(enable);
|
||||
}
|
||||
});
|
||||
|
||||
mCloseImageView.setOnClickListener(view -> {
|
||||
mCurrentIntent = InteractLiveIntent.INTENT_FINISH;
|
||||
showInteractLiveDialog(getResources().getString(R.string.interact_live_leave_room_tips), false);
|
||||
});
|
||||
|
||||
//开始 PK
|
||||
mConnectTextView.setOnClickListener(view -> {
|
||||
if (mPKController.isPKing()) {
|
||||
mCurrentIntent = InteractLiveIntent.INTENT_STOP_PULL;
|
||||
showInteractLiveDialog(getResources().getString(R.string.pk_live_connect_finish_tips), false);
|
||||
} else {
|
||||
showInteractLiveDialog(null, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void initData() {
|
||||
InteractiveUserData anchorUserData = (InteractiveUserData) getIntent().getSerializableExtra(InteractiveConstants.DATA_TYPE_INTERACTIVE_USER_DATA);
|
||||
mPKController = new PKController(this, anchorUserData);
|
||||
mPKController.setPKLivePushPullListener(new InteractLivePushPullListener() {
|
||||
@Override
|
||||
public void onPullSuccess(InteractiveUserData userData) {
|
||||
super.onPullSuccess(userData);
|
||||
mPKController.setPKLiveMixTranscoding(true);
|
||||
changeFrameLayoutViewVisible(true);
|
||||
updateConnectTextView(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPullError(InteractiveUserData userData, AlivcLivePlayError errorType, String errorMsg) {
|
||||
super.onPullError(userData, errorType, errorMsg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPullStop(InteractiveUserData userData) {
|
||||
super.onPullStop(userData);
|
||||
mPKController.setPKLiveMixTranscoding(false);
|
||||
changeFrameLayoutViewVisible(false);
|
||||
updateConnectTextView(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPushSuccess() {
|
||||
super.onPushSuccess();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPushError() {
|
||||
super.onPushError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectionLost() {
|
||||
super.onConnectionLost();
|
||||
|
||||
// 断网后停止本地录制
|
||||
mPKController.stopLocalRecord();
|
||||
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mConnectionLostTipsView.show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceiveSEIMessage(int payload, byte[] data) {
|
||||
super.onReceiveSEIMessage(payload, data);
|
||||
mSeiMessageView.appendMessage("[rtc] payload=" + payload + ", " + new String(data, StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceiveSEIDelay(String src, String type, String msg) {
|
||||
super.onReceiveSEIDelay(src, type, msg);
|
||||
mSeiMessageView.appendMessage("[" + src + "][" + type + "][" + msg + "ms]");
|
||||
}
|
||||
});
|
||||
|
||||
mPKController.startPush(mOwnerFrameLayout);
|
||||
|
||||
mOwnerInfoView.setUserData(anchorUserData);
|
||||
mHomeIdTextView.setText(anchorUserData.channelId);
|
||||
}
|
||||
|
||||
private void showInteractLiveDialog(String content, boolean showInputView) {
|
||||
InteractiveCommonInputView interactiveCommonInputView = new InteractiveCommonInputView(PKLiveActivity.this);
|
||||
interactiveCommonInputView.setViewType(InteractiveCommonInputView.ViewType.PK);
|
||||
interactiveCommonInputView.showInputView(content, showInputView);
|
||||
mAUILiveDialog.setContentView(interactiveCommonInputView);
|
||||
mAUILiveDialog.show();
|
||||
|
||||
interactiveCommonInputView.setOnInteractLiveTipsViewListener(new InteractLiveTipsViewListener() {
|
||||
@Override
|
||||
public void onCancel() {
|
||||
if (mAUILiveDialog.isShowing()) {
|
||||
mAUILiveDialog.dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfirm() {
|
||||
//退出直播
|
||||
if (mCurrentIntent == InteractLiveIntent.INTENT_FINISH) {
|
||||
finish();
|
||||
} else if (mCurrentIntent == InteractLiveIntent.INTENT_STOP_PULL) {
|
||||
mAUILiveDialog.dismiss();
|
||||
mPKController.stopPK();
|
||||
mOtherUserData = null;
|
||||
updateConnectTextView(false);
|
||||
changeFrameLayoutViewVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInputConfirm(InteractiveUserData userData) {
|
||||
mAUILiveDialog.dismiss();
|
||||
if (userData != null && !TextUtils.isEmpty(userData.userId) && !TextUtils.isEmpty(userData.channelId)) {
|
||||
mPKController.setPKOtherInfo(userData);
|
||||
mPKController.startPK(userData, mOtherFrameLayout);
|
||||
mOtherUserData = userData;
|
||||
mOtherInfoView.setUserInfo(userData.userId, userData.channelId);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void updateConnectTextView(boolean connecting) {
|
||||
if (connecting) {
|
||||
mShowConnectTextView.setVisibility(View.VISIBLE);
|
||||
mConnectTextView.setText(getResources().getString(R.string.pk_stop_connect));
|
||||
mConnectTextView.setBackground(getResources().getDrawable(R.drawable.shape_interact_live_un_connect_btn_bg));
|
||||
} else {
|
||||
mShowConnectTextView.setVisibility(View.GONE);
|
||||
mConnectTextView.setText(getResources().getString(R.string.pk_start_connect));
|
||||
mConnectTextView.setBackground(getResources().getDrawable(R.drawable.shape_pysh_btn_bg));
|
||||
}
|
||||
}
|
||||
|
||||
private void changeFrameLayoutViewVisible(boolean isShowSurfaceView) {
|
||||
mOtherFrameLayout.setVisibility(isShowSurfaceView ? View.VISIBLE : View.INVISIBLE);
|
||||
mUnConnectFrameLayout.setVisibility(isShowSurfaceView ? View.INVISIBLE : View.VISIBLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
mPKController.release();
|
||||
}
|
||||
|
||||
private boolean checkMediaFileExists() {
|
||||
File yuvFile = ResourcesConst.localCaptureYUVFilePath(getBaseContext());
|
||||
if (!yuvFile.exists()) {
|
||||
ToastUtils.show("Please ensure that the local yuv files have been downloaded!");
|
||||
return false;
|
||||
}
|
||||
File pcmFile = ResourcesConst.localCapturePCMFilePath(getBaseContext());
|
||||
if (!pcmFile.exists()) {
|
||||
ToastUtils.show("Please ensure that the local pcm files have been downloaded!");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
package com.alivc.live.interactive_pk;
|
||||
|
||||
import static com.alivc.live.interactive_common.utils.LivePushGlobalConfig.mAlivcLivePushConfig;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import com.alivc.live.interactive_common.InteractLiveBaseManager;
|
||||
import com.alivc.live.interactive_common.InteractiveBaseUtil;
|
||||
import com.alivc.live.interactive_common.bean.InteractiveUserData;
|
||||
import com.alivc.live.player.AlivcLivePlayer;
|
||||
import com.alivc.live.pusher.AlivcLiveMixStream;
|
||||
import com.alivc.live.pusher.AlivcLiveMixStreamType;
|
||||
import com.alivc.live.pusher.AlivcLiveTranscodingConfig;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class PKLiveManager extends InteractLiveBaseManager {
|
||||
|
||||
public void resumeAudioPlaying(String key) {
|
||||
AlivcLivePlayer alivcLivePlayer = mInteractiveLivePlayerMap.get(key);
|
||||
if (alivcLivePlayer != null) {
|
||||
alivcLivePlayer.resumeAudioPlaying();
|
||||
}
|
||||
}
|
||||
|
||||
public void pauseAudioPlaying(String key) {
|
||||
AlivcLivePlayer alivcLivePlayer = mInteractiveLivePlayerMap.get(key);
|
||||
if (alivcLivePlayer != null) {
|
||||
alivcLivePlayer.pauseAudioPlaying();
|
||||
}
|
||||
}
|
||||
|
||||
// PK场景下设置混流
|
||||
public void setLiveMixTranscodingConfig(InteractiveUserData anchorUserData, InteractiveUserData otherUserData, boolean pkMute) {
|
||||
if (anchorUserData == null || TextUtils.isEmpty(anchorUserData.channelId) || TextUtils.isEmpty(anchorUserData.userId)) {
|
||||
clearLiveMixTranscodingConfig();
|
||||
return;
|
||||
}
|
||||
|
||||
if (otherUserData == null || TextUtils.isEmpty(otherUserData.channelId) || TextUtils.isEmpty(otherUserData.userId)) {
|
||||
clearLiveMixTranscodingConfig();
|
||||
return;
|
||||
}
|
||||
|
||||
if (mAlivcLivePushConfig == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mAlivcLivePusher == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ArrayList<AlivcLiveMixStream> mixStreams = new ArrayList<>();
|
||||
|
||||
AlivcLiveMixStream anchorMixStream = new AlivcLiveMixStream();
|
||||
anchorMixStream.userId = anchorUserData.userId;
|
||||
anchorMixStream.x = 0;
|
||||
anchorMixStream.y = 0;
|
||||
anchorMixStream.width = mAlivcLivePushConfig.getWidth() / 2;
|
||||
anchorMixStream.height = mAlivcLivePushConfig.getHeight() / 2;
|
||||
anchorMixStream.zOrder = 1;
|
||||
anchorMixStream.backgroundImageUrl = "https://alivc-demo-cms.alicdn.com/versionProduct/resources/pictures/siheng.jpg";
|
||||
|
||||
mixStreams.add(anchorMixStream);
|
||||
|
||||
if (mAudienceFrameLayout != null) {
|
||||
AlivcLiveMixStream otherMixStream = new AlivcLiveMixStream();
|
||||
otherMixStream.userId = otherUserData.userId;
|
||||
otherMixStream.x = mAlivcLivePushConfig.getWidth() / 2;
|
||||
otherMixStream.y = 0;
|
||||
otherMixStream.width = mAlivcLivePushConfig.getWidth() / 2;
|
||||
otherMixStream.height = mAlivcLivePushConfig.getHeight() / 2;
|
||||
otherMixStream.zOrder = 2;
|
||||
otherMixStream.mixStreamType = pkMute ? AlivcLiveMixStreamType.PURE_VIDEO : AlivcLiveMixStreamType.AUDIO_VIDEO;
|
||||
otherMixStream.mixSourceType = InteractiveBaseUtil.covertVideoStreamType2MixSourceType(otherUserData.videoStreamType);
|
||||
otherMixStream.backgroundImageUrl = "https://alivc-demo-cms.alicdn.com/versionProduct/resources/pictures/lantu.jpg";
|
||||
|
||||
mixStreams.add(otherMixStream);
|
||||
}
|
||||
|
||||
AlivcLiveTranscodingConfig transcodingConfig = new AlivcLiveTranscodingConfig();
|
||||
transcodingConfig.mixStreams = mixStreams;
|
||||
mAlivcLivePusher.setLiveMixTranscodingConfig(transcodingConfig);
|
||||
}
|
||||
|
||||
// 多人PK场景下添加混流
|
||||
public void addAnchorMixTranscodingConfig(InteractiveUserData anchorUserData) {
|
||||
if (anchorUserData == null || TextUtils.isEmpty(anchorUserData.channelId) || TextUtils.isEmpty(anchorUserData.userId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mAlivcLivePushConfig == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mAlivcLivePusher == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
AlivcLiveMixStream anchorMixStream = new AlivcLiveMixStream();
|
||||
anchorMixStream.userId = anchorUserData.userId;
|
||||
anchorMixStream.x = 0;
|
||||
anchorMixStream.y = 0;
|
||||
anchorMixStream.width = mAlivcLivePushConfig.getWidth();
|
||||
anchorMixStream.height = mAlivcLivePushConfig.getHeight();
|
||||
anchorMixStream.zOrder = 1;
|
||||
anchorMixStream.mixSourceType = InteractiveBaseUtil.covertVideoStreamType2MixSourceType(anchorUserData.videoStreamType);
|
||||
anchorMixStream.backgroundImageUrl = "https://alivc-demo-cms.alicdn.com/versionProduct/resources/pictures/siheng.jpg";
|
||||
|
||||
mMultiInteractLiveMixStreamsArray.add(anchorMixStream);
|
||||
mMixInteractLiveTranscodingConfig.mixStreams = mMultiInteractLiveMixStreamsArray;
|
||||
|
||||
mAlivcLivePusher.setLiveMixTranscodingConfig(mMixInteractLiveTranscodingConfig);
|
||||
}
|
||||
|
||||
// 多人连麦混流静音
|
||||
public void muteAnchorMultiStream(InteractiveUserData audienceUserData, boolean mute) {
|
||||
if (audienceUserData == null || TextUtils.isEmpty(audienceUserData.channelId) || TextUtils.isEmpty(audienceUserData.userId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
AlivcLiveMixStream mixStream = findMixStreamByUserData(audienceUserData);
|
||||
if (mixStream == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
mixStream.mixStreamType = mute ? AlivcLiveMixStreamType.PURE_VIDEO : AlivcLiveMixStreamType.AUDIO_VIDEO;
|
||||
if (mAlivcLivePusher != null) {
|
||||
mAlivcLivePusher.setLiveMixTranscodingConfig(mMixInteractLiveTranscodingConfig);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加混流配置
|
||||
*
|
||||
* @param audienceUserData 观众信息
|
||||
* @param frameLayout 观众 frameLayout(渲染 View 的 ViewGroup,用于计算混流位置)
|
||||
*/
|
||||
public void addAudienceMixTranscodingConfig(InteractiveUserData audienceUserData, FrameLayout frameLayout) {
|
||||
if (audienceUserData == null || TextUtils.isEmpty(audienceUserData.channelId) || TextUtils.isEmpty(audienceUserData.userId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mAlivcLivePushConfig == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mAlivcLivePusher == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
AlivcLiveMixStream audienceMixStream = new AlivcLiveMixStream();
|
||||
audienceMixStream.userId = audienceUserData.userId;
|
||||
int size = mMultiInteractLiveMixStreamsArray.size() - 1;
|
||||
audienceMixStream.x = size % 3 * frameLayout.getWidth() / 3;
|
||||
audienceMixStream.y = size / 3 * frameLayout.getHeight() / 3;
|
||||
audienceMixStream.width = frameLayout.getWidth() / 3;
|
||||
audienceMixStream.height = frameLayout.getHeight() / 3;
|
||||
audienceMixStream.zOrder = 2;
|
||||
audienceMixStream.mixSourceType = InteractiveBaseUtil.covertVideoStreamType2MixSourceType(audienceUserData.videoStreamType);
|
||||
audienceMixStream.backgroundImageUrl = "https://alivc-demo-cms.alicdn.com/versionProduct/resources/pictures/yiliang.png";
|
||||
|
||||
mMultiInteractLiveMixStreamsArray.add(audienceMixStream);
|
||||
mMixInteractLiveTranscodingConfig.mixStreams = mMultiInteractLiveMixStreamsArray;
|
||||
|
||||
mAlivcLivePusher.setLiveMixTranscodingConfig(mMixInteractLiveTranscodingConfig);
|
||||
}
|
||||
|
||||
// 移除混流
|
||||
public void removeLiveMixTranscodingConfig(InteractiveUserData userData) {
|
||||
if (userData == null || TextUtils.isEmpty(userData.channelId) || TextUtils.isEmpty(userData.userId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
AlivcLiveMixStream mixStream = findMixStreamByUserData(userData);
|
||||
if (mixStream == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
mMultiInteractLiveMixStreamsArray.remove(mixStream);
|
||||
|
||||
//多人 PK 混流,结束 PK 后,重新排版混流界面,防止出现覆盖现象
|
||||
int size = mMultiInteractLiveMixStreamsArray.size() - 1;
|
||||
for (int i = 1; i < mMultiInteractLiveMixStreamsArray.size(); i++) {
|
||||
AlivcLiveMixStream alivcLiveMixStream = mMultiInteractLiveMixStreamsArray.get(i);
|
||||
alivcLiveMixStream.x = ((size - i) % 3) * alivcLiveMixStream.width;
|
||||
alivcLiveMixStream.y = (size - i) / 3 * alivcLiveMixStream.height;
|
||||
alivcLiveMixStream.width = alivcLiveMixStream.width;
|
||||
alivcLiveMixStream.height = alivcLiveMixStream.height;
|
||||
}
|
||||
|
||||
//Array 中只剩主播 id,说明无人连麦
|
||||
if (mMultiInteractLiveMixStreamsArray.size() == 1 && mMultiInteractLiveMixStreamsArray.get(0).userId.equals(userData.userId)) {
|
||||
clearLiveMixTranscodingConfig();
|
||||
} else {
|
||||
mMixInteractLiveTranscodingConfig.mixStreams = mMultiInteractLiveMixStreamsArray;
|
||||
if (mAlivcLivePusher != null) {
|
||||
mAlivcLivePusher.setLiveMixTranscodingConfig(mMixInteractLiveTranscodingConfig);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="centerCrop"
|
||||
android:src="@drawable/ic_bg_pk" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_home_id"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="44dp"
|
||||
android:textColor="#FCFCFD"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_show_connect"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/pk_live_connecting"
|
||||
android:textColor="#FCFCFD"
|
||||
android:textSize="12sp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintEnd_toEndOf="@id/tv_home_id"
|
||||
app:layout_constraintStart_toStartOf="@id/tv_home_id"
|
||||
app:layout_constraintTop_toBottomOf="@id/tv_home_id" />
|
||||
|
||||
<com.alivc.live.commonui.widgets.ResizableFrameLayout
|
||||
android:id="@+id/frame_owner"
|
||||
android:layout_width="150dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginTop="32dp"
|
||||
app:layout_constraintDimensionRatio="w,16:9"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/tv_show_connect" />
|
||||
|
||||
<com.alivc.live.interactive_common.widget.RoomAndUserInfoView
|
||||
android:id="@+id/owner_info_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
app:layout_constraintStart_toStartOf="@id/frame_owner"
|
||||
app:layout_constraintTop_toTopOf="@id/frame_owner" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_icon_pk"
|
||||
android:layout_width="66dp"
|
||||
android:layout_height="23dp"
|
||||
android:src="@drawable/ic_pk_sign"
|
||||
app:layout_constraintEnd_toEndOf="@id/frame_owner"
|
||||
app:layout_constraintStart_toStartOf="@id/frame_owner"
|
||||
app:layout_constraintTop_toBottomOf="@id/frame_owner" />
|
||||
|
||||
<com.alivc.live.commonui.messageview.AutoScrollMessagesView
|
||||
android:id="@+id/sei_receive_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="120dp"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_marginTop="20dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/iv_close" />
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginTop="10dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/iv_icon_pk">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recycler_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:overScrollMode="never" />
|
||||
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
||||
<com.alivc.live.interactive_common.widget.InteractiveRoomControlView
|
||||
android:id="@+id/interactive_setting_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="20dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/tv_home_id" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_close"
|
||||
android:layout_width="26dp"
|
||||
android:layout_height="26dp"
|
||||
android:layout_marginStart="20dp"
|
||||
android:src="@drawable/ic_close_white"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/tv_home_id" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
229
LiveInteractive/live_pk/src/main/res/layout/activity_pklive.xml
Normal file
@@ -0,0 +1,229 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="centerCrop"
|
||||
android:src="@drawable/ic_bg_pk" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_close"
|
||||
android:layout_width="26dp"
|
||||
android:layout_height="26dp"
|
||||
android:layout_marginStart="20dp"
|
||||
android:src="@drawable/ic_close_white"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/tv_home_id" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_home_id"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="44dp"
|
||||
android:textColor="#FCFCFD"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_show_connect"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/pk_live_connecting"
|
||||
android:textColor="#FCFCFD"
|
||||
android:textSize="12sp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintEnd_toEndOf="@id/tv_home_id"
|
||||
app:layout_constraintStart_toStartOf="@id/tv_home_id"
|
||||
app:layout_constraintTop_toBottomOf="@id/tv_home_id" />
|
||||
|
||||
<com.alivc.live.commonui.widgets.ResizableFrameLayout
|
||||
android:id="@+id/frame_owner"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="332dp"
|
||||
android:layout_marginTop="42dp"
|
||||
app:layout_constraintEnd_toStartOf="@id/render_container"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/tv_show_connect" />
|
||||
|
||||
<com.alivc.live.commonui.widgets.ResizableFrameLayout
|
||||
android:id="@+id/render_container"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="332dp"
|
||||
app:layout_constraintBottom_toBottomOf="@id/frame_owner"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/frame_owner"
|
||||
app:layout_constraintTop_toTopOf="@id/frame_owner" />
|
||||
|
||||
<com.alivc.live.interactive_common.widget.RoomAndUserInfoView
|
||||
android:id="@+id/owner_info_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="10dp"
|
||||
app:layout_constraintStart_toStartOf="@id/frame_owner"
|
||||
app:layout_constraintTop_toTopOf="@id/frame_owner" />
|
||||
|
||||
<com.alivc.live.interactive_common.widget.InteractivePaneControlView
|
||||
android:id="@+id/owner_ctrl_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintStart_toStartOf="@+id/owner_info_view"
|
||||
app:layout_constraintTop_toBottomOf="@+id/owner_info_view" />
|
||||
|
||||
<com.alivc.live.interactive_common.widget.RoomAndUserInfoView
|
||||
android:id="@+id/other_info_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="10dp"
|
||||
app:layout_constraintStart_toStartOf="@id/render_container"
|
||||
app:layout_constraintTop_toTopOf="@id/render_container" />
|
||||
|
||||
<com.alivc.live.interactive_common.widget.InteractivePaneControlView
|
||||
android:id="@+id/other_ctrl_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintStart_toStartOf="@+id/other_info_view"
|
||||
app:layout_constraintTop_toBottomOf="@+id/other_info_view" />
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/fl_un_connect"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:background="#23262F"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/render_container"
|
||||
app:layout_constraintEnd_toEndOf="@+id/render_container"
|
||||
app:layout_constraintStart_toStartOf="@+id/render_container"
|
||||
app:layout_constraintTop_toTopOf="@+id/render_container">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:src="@drawable/ic_interact_live_un_connect_bg" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<com.alivc.live.commonui.messageview.AutoScrollMessagesView
|
||||
android:id="@+id/sei_receive_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="120dp"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:layout_marginBottom="20dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:layout_width="66dp"
|
||||
android:layout_height="23dp"
|
||||
android:src="@drawable/ic_pk_sign"
|
||||
app:layout_constraintEnd_toEndOf="@id/frame_owner"
|
||||
app:layout_constraintStart_toEndOf="@id/frame_owner"
|
||||
app:layout_constraintTop_toTopOf="@id/frame_owner" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/render_container">
|
||||
|
||||
<com.alivc.live.commonui.widgets.LivePushTextSwitch
|
||||
android:id="@+id/btn_show_custom_message"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<com.alivc.live.commonui.widgets.LivePushTextSwitch
|
||||
android:id="@+id/btn_local_record"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<com.alivc.live.commonui.widgets.LivePushTextSwitch
|
||||
android:id="@+id/btn_external_audio_stream"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<com.alivc.live.commonui.widgets.LivePushTextSwitch
|
||||
android:id="@+id/btn_external_video_stream"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<com.alivc.live.commonui.widgets.LivePushTextSwitch
|
||||
android:id="@+id/btn_push_dual_audio"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<com.alivc.live.commonui.widgets.LivePushTextSwitch
|
||||
android:id="@+id/btn_push_screen_share"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<com.alivc.live.commonui.widgets.LivePushTextSwitch
|
||||
android:id="@+id/btn_change_resolution_and_fps"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<com.alivc.live.commonui.widgets.LivePushTextSwitch
|
||||
android:id="@+id/btn_force_ear_monitor"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_connect"
|
||||
android:layout_width="94dp"
|
||||
android:layout_height="30dp"
|
||||
android:layout_marginTop="28dp"
|
||||
android:background="@drawable/shape_pysh_btn_bg"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center"
|
||||
android:includeFontPadding="false"
|
||||
android:maxLines="1"
|
||||
android:text="@string/pk_start_connect"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/render_container" />
|
||||
|
||||
<com.alivc.live.interactive_common.widget.InteractiveRoomControlView
|
||||
android:id="@+id/interactive_setting_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="20dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/tv_home_id" />
|
||||
|
||||
<com.alivc.live.commonui.seiview.LivePusherSEIView
|
||||
android:id="@+id/sei_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="10dp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/tv_connect" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_beauty"
|
||||
android:layout_width="22dp"
|
||||
android:layout_height="18dp"
|
||||
android:layout_marginTop="20dp"
|
||||
android:scaleType="centerCrop"
|
||||
android:src="@drawable/beauty_selector"
|
||||
app:layout_constraintEnd_toEndOf="@id/interactive_setting_view"
|
||||
app:layout_constraintStart_toStartOf="@id/interactive_setting_view"
|
||||
app:layout_constraintTop_toBottomOf="@id/interactive_setting_view" />
|
||||
|
||||
<com.aliyunsdk.queen.menu.QueenBeautyMenu
|
||||
android:id="@+id/beauty_beauty_menuPanel"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -0,0 +1,71 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="5dp">
|
||||
|
||||
<com.alivc.live.commonui.widgets.ResizableFrameLayout
|
||||
android:id="@+id/render_container"
|
||||
android:layout_width="100dp"
|
||||
android:layout_height="0dp"
|
||||
android:background="@color/alivc_color_black"
|
||||
app:layout_constraintDimensionRatio="w,16:9"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
</com.alivc.live.commonui.widgets.ResizableFrameLayout>
|
||||
|
||||
<com.alivc.live.interactive_common.widget.RoomAndUserInfoView
|
||||
android:id="@+id/view_userinfo"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="5dp"
|
||||
app:layout_constraintStart_toStartOf="@id/render_container"
|
||||
app:layout_constraintTop_toTopOf="@id/render_container" />
|
||||
|
||||
<com.alivc.live.interactive_common.widget.InteractivePaneControlView
|
||||
android:id="@+id/view_ctrl"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintStart_toStartOf="@+id/view_userinfo"
|
||||
app:layout_constraintTop_toBottomOf="@+id/view_userinfo" />
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/fl_un_connect"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:background="#23262F"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/render_container"
|
||||
app:layout_constraintEnd_toEndOf="@+id/render_container"
|
||||
app:layout_constraintStart_toStartOf="@+id/render_container"
|
||||
app:layout_constraintTop_toTopOf="@+id/render_container">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:src="@drawable/ic_interact_live_un_connect_bg" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_connect"
|
||||
android:layout_width="94dp"
|
||||
android:layout_height="30dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:background="@drawable/shape_pysh_btn_bg"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center"
|
||||
android:includeFontPadding="false"
|
||||
android:maxLines="1"
|
||||
android:text="@string/pk_start_connect"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/render_container" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||