集成完直播后提交代码
This commit is contained in:
1
LiveInteractive/live_pk/.gitignore
vendored
Normal file
1
LiveInteractive/live_pk/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/build
|
||||
43
LiveInteractive/live_pk/build.gradle
Normal file
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
0
LiveInteractive/live_pk/consumer-rules.pro
Normal file
26
LiveInteractive/live_pk/src/main/AndroidManifest.xml
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
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>
|
||||
Reference in New Issue
Block a user