集成完直播后提交代码

This commit is contained in:
xuhuixiang
2026-02-06 14:55:21 +08:00
commit ea9ffa06ff
960 changed files with 75063 additions and 0 deletions

1
LiveInteractive/live_pk/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/build

View 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')
}

View 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>

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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();
}
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}
}
}

View File

@@ -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>

View 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>

View File

@@ -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>