集成完直播后提交代码
This commit is contained in:
1
LiveInteractive/live_interactive/.gitignore
vendored
Normal file
1
LiveInteractive/live_interactive/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/build
|
||||
51
LiveInteractive/live_interactive/build.gradle
Normal file
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
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>
|
||||
Reference in New Issue
Block a user