集成完直播后提交代码

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

View File

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

View File

@@ -0,0 +1,44 @@
plugins {
id 'com.android.library'
}
android {
compileSdkVersion androidCompileSdkVersion
buildToolsVersion androidBuildToolsVersion
defaultConfig {
minSdkVersion androidMinSdkVersion
targetSdkVersion androidTargetSdkVersion
versionCode 1
versionName "1.0"
consumerProguardFiles "consumer-rules.pro"
javaCompileOptions {
annotationProcessorOptions {
arguments = [AROUTER_MODULE_NAME :'interactiveBareStream' ]
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
implementation externalAndroidDesign
implementation externalSimpleZXing
api project(':LiveInteractive:live_interactive_common')
implementation externalARouter
annotationProcessor externalARouterCompiler
}

View File

@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

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.barestream_interactive" >
<application>
<activity
android:name=".InteractiveBareActivity"
android:alwaysRetainTaskState="true"
android:launchMode="singleTop"
android:screenOrientation="portrait"
android:theme="@style/Theme.AppCompat.Light.NoActionBar" />
<activity
android:name=".InteractiveMultiBareActivity"
android:alwaysRetainTaskState="true"
android:launchMode="singleTop"
android:screenOrientation="portrait"
android:theme="@style/Theme.AppCompat.Light.NoActionBar" />
<activity
android:name=".InteractiveInputURLActivity"
android:alwaysRetainTaskState="true"
android:launchMode="singleTop"
android:screenOrientation="portrait"
android:theme="@style/Theme.AppCompat.Light.NoActionBar" />
</application>
</manifest>

View File

@@ -0,0 +1,159 @@
package com.alivc.live.barestream_interactive;
import android.content.Context;
import android.widget.FrameLayout;
import com.alivc.live.commonbiz.LocalStreamReader;
import com.alivc.live.commonbiz.ResourcesConst;
import com.alivc.live.interactive_common.InteractLiveBaseManager;
import com.alivc.live.interactive_common.InteractiveMode;
import com.alivc.live.interactive_common.bean.InteractiveUserData;
import com.alivc.live.interactive_common.listener.InteractLivePushPullListener;
import com.alivc.live.interactive_common.utils.LivePushGlobalConfig;
import com.alivc.live.pusher.AlivcResolutionEnum;
import java.io.File;
class BareStreamController {
private final InteractLiveBaseManager mInteractLiveManager;
private final Context mContext;
private final LocalStreamReader mLocalStreamReader;
//主播预览 View
private FrameLayout mAnchorRenderView;
//观众连麦预览 View
private FrameLayout mViewerRenderView;
// 主播连麦推流信息
private InteractiveUserData mPushUserData;
// 观众连麦拉流信息
private InteractiveUserData mPullUserData;
public BareStreamController(Context context) {
this.mContext = context;
AlivcResolutionEnum resolution = LivePushGlobalConfig.mAlivcLivePushConfig.getResolution();
int width = AlivcResolutionEnum.getResolutionWidth(resolution, LivePushGlobalConfig.mAlivcLivePushConfig.getLivePushMode());
int height = AlivcResolutionEnum.getResolutionHeight(resolution, LivePushGlobalConfig.mAlivcLivePushConfig.getLivePushMode());
mLocalStreamReader = new LocalStreamReader.Builder()
.setVideoWith(width)
.setVideoHeight(height)
.setVideoStride(width)
.setVideoSize(height * width * 3 / 2)
.setVideoRotation(0)
.setAudioSampleRate(44100)
.setAudioChannel(1)
.setAudioBufferSize(2048)
.build();
mInteractLiveManager = new InteractLiveBaseManager();
mInteractLiveManager.init(context, InteractiveMode.BARE_STREAM);
}
/**
* 设置主播预览 View
*
* @param frameLayout 主播预览 View
*/
public void setAnchorRenderView(FrameLayout frameLayout) {
this.mAnchorRenderView = frameLayout;
}
/**
* 设置观众预览 View
*
* @param frameLayout 观众预览 View
*/
public void setViewerRenderView(FrameLayout frameLayout) {
this.mViewerRenderView = frameLayout;
}
/**
* 开始直播
*/
public void startPush(InteractiveUserData userData) {
mPushUserData = userData;
externAV();
mInteractLiveManager.startPreviewAndPush(mPushUserData, mAnchorRenderView, true);
}
public void stopPreview() {
mInteractLiveManager.stopPreview();
}
public void stopPush() {
mPushUserData = null;
mInteractLiveManager.stopPush();
}
public void stopCamera() {
mInteractLiveManager.stopCamera();
}
private void externAV() {
if (LivePushGlobalConfig.mAlivcLivePushConfig.isExternMainStream()) {
File yuvFile = ResourcesConst.localCaptureYUVFilePath(mContext);
mLocalStreamReader.readYUVData(yuvFile, (buffer, pts, videoWidth, videoHeight, videoStride, videoSize, videoRotation) -> {
mInteractLiveManager.inputStreamVideoData(buffer, videoWidth, videoHeight, videoStride, videoSize, pts, videoRotation);
});
File pcmFile = ResourcesConst.localCapturePCMFilePath(mContext);
mLocalStreamReader.readPCMData(pcmFile, (buffer, length, pts, audioSampleRate, audioChannel) -> {
mInteractLiveManager.inputStreamAudioData(buffer, length, audioSampleRate, audioChannel, pts);
});
}
}
// 开始拉流
public void startPull(InteractiveUserData userData) {
mPullUserData = userData;
mInteractLiveManager.setPullView(mPullUserData, mViewerRenderView, false);
mInteractLiveManager.startPullRTCStream(mPullUserData);
}
// 结束拉流
public void stopPull() {
mInteractLiveManager.stopPullRTCStream(mPullUserData);
mPullUserData = null;
}
/**
* 主播是否正在连麦
*
* @return true:正在连麦 false:没有连麦
*/
public boolean isPulling() {
return mInteractLiveManager.isPulling(mPullUserData);
}
public void release() {
mInteractLiveManager.release();
mLocalStreamReader.stopYUV();
mLocalStreamReader.stopPcm();
}
public void setInteractLivePushPullListener(InteractLivePushPullListener listener) {
mInteractLiveManager.setInteractLivePushPullListener(listener);
}
public void switchCamera() {
mInteractLiveManager.switchCamera();
}
public void enableSpeakerPhone(boolean enable) {
mInteractLiveManager.enableSpeakerPhone(enable);
}
public void setMute(boolean b) {
mInteractLiveManager.setMute(b);
}
public void enableAudioCapture(boolean enable) {
mInteractLiveManager.enableAudioCapture(enable);
}
public void muteLocalCamera(boolean muteLocalCamera) {
mInteractLiveManager.muteLocalCamera(muteLocalCamera);
}
public void enableLocalCamera(boolean enable) {
mInteractLiveManager.enableLocalCamera(enable);
}
}

View File

@@ -0,0 +1,399 @@
package com.alivc.live.barestream_interactive;
import android.Manifest;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Color;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import com.acker.simplezxing.activity.CaptureActivity;
import com.alivc.live.commonui.utils.StatusBarUtil;
import com.alivc.live.commonutils.ToastUtils;
import com.alivc.live.interactive_common.bean.InteractiveUserData;
import com.alivc.live.interactive_common.listener.InteractLivePushPullListener;
import com.alivc.live.interactive_common.listener.InteractLiveTipsViewListener;
import com.alivc.live.interactive_common.utils.InteractLiveIntent;
import com.alivc.live.commonui.avdialog.AUILiveDialog;
import com.alivc.live.interactive_common.widget.ConnectionLostTipsView;
import com.alivc.live.interactive_common.widget.InteractiveCommonInputView;
import com.alivc.live.interactive_common.widget.InteractiveConnectView;
import com.alivc.live.interactive_common.widget.InteractiveRoomControlView;
import com.alivc.live.player.annotations.AlivcLivePlayError;
public class InteractiveBareActivity extends AppCompatActivity {
private static final int REQ_CODE_PERMISSION = 0x1111;
private static final int REQ_CODE_PUSH_URL = 0x2222;
private static final int REQ_CODE_PULL_URL = 0x3333;
public static final String DATA_INTERACTIVE_PUSH_URL = "data_interactive_push_url";
public static final String DATA_INTERACTIVE_PULL_URL = "data_interactive_pull_url";
private AUILiveDialog mAUILiveDialog;
//Dialog 弹窗的意图
private InteractLiveIntent mCurrentIntent;
private ImageView mCloseImageView;
private TextView mShowConnectIdTextView;
//大窗口
private FrameLayout mBigFrameLayout;
//小窗口
private FrameLayout mSmallFrameLayout;
private ConnectionLostTipsView mConnectionLostTipsView;
//输入 URL 方式连麦
private InteractiveUserData mPushUserData;
private InteractiveUserData mPullUserData;
private InteractiveCommonInputView commonInputView;
private InteractiveConnectView mInteractiveConnectView;
private InteractiveRoomControlView mInteractiveRoomControlView;
private BareStreamController mBareStreamController;
private EditText mInteractivePushUrlEditText;
private ImageView mPushUrlQrImageView;
private TextView mStartPushTextView;
private boolean mIsPushing;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
StatusBarUtil.translucent(this, Color.TRANSPARENT);
setContentView(R.layout.activity_interactive_bare);
InteractiveUserData pushUserData = new InteractiveUserData();
pushUserData.url = getIntent().getStringExtra(DATA_INTERACTIVE_PUSH_URL);
mPushUserData = pushUserData;
InteractiveUserData pullUserData = new InteractiveUserData();
pullUserData.url = getIntent().getStringExtra(DATA_INTERACTIVE_PULL_URL);
mPullUserData = pullUserData;
mBareStreamController = new BareStreamController(this);
initView();
initListener();
initData();
}
private void initData() {
mInteractivePushUrlEditText.setText(mPushUserData != null ? mPushUserData.url : "");
mBareStreamController.setAnchorRenderView(mBigFrameLayout);
mBareStreamController.setViewerRenderView(mSmallFrameLayout);
}
private void initView() {
mAUILiveDialog = new AUILiveDialog(this);
mInteractiveConnectView = findViewById(R.id.connect_view);
mCloseImageView = findViewById(R.id.iv_close);
mShowConnectIdTextView = findViewById(R.id.tv_show_connect);
mBigFrameLayout = findViewById(R.id.big_fl);
mSmallFrameLayout = findViewById(R.id.small_fl);
mInteractiveRoomControlView = findViewById(R.id.interactive_setting_view);
mConnectionLostTipsView = new ConnectionLostTipsView(this);
mPushUrlQrImageView = findViewById(R.id.iv_qr_push_url);
mStartPushTextView = findViewById(R.id.tv_start_push);
mInteractivePushUrlEditText = findViewById(R.id.et_interactive_push_url);
mInteractiveConnectView.setDefaultText(getResources().getString(R.string.interactive_bare_stream_start_pull));
}
private void initListener() {
mPushUrlQrImageView.setOnClickListener(view -> {
startQr(REQ_CODE_PUSH_URL);
});
mStartPushTextView.setOnClickListener(view -> {
if (mIsPushing) {
mBareStreamController.stopPush();
changePushState(false);
} else {
mPushUserData.url = mInteractivePushUrlEditText.getText().toString();
mBareStreamController.startPush(mPushUserData);
}
});
mConnectionLostTipsView.setConnectionLostListener(() -> runOnUiThread(() -> finish()));
mBareStreamController.setInteractLivePushPullListener(new InteractLivePushPullListener() {
@Override
public void onPullSuccess(InteractiveUserData userData) {
super.onPullSuccess(userData);
changeSmallSurfaceViewVisible(true);
updateConnectTextView(true);
}
@Override
public void onPullError(InteractiveUserData userData, AlivcLivePlayError errorType, String errorMsg) {
super.onPullError(userData, errorType, errorMsg);
runOnUiThread(() -> {
changeSmallSurfaceViewVisible(false);
mBareStreamController.stopPull();
updateConnectTextView(false);
ToastUtils.show(getResources().getString(R.string.interact_live_viewer_left));
});
}
@Override
public void onPullStop(InteractiveUserData userData) {
super.onPullStop(userData);
runOnUiThread(() -> {
changeSmallSurfaceViewVisible(false);
updateConnectTextView(false);
});
}
@Override
public void onPushSuccess() {
super.onPushSuccess();
changePushState(true);
}
@Override
public void onPushError() {
super.onPushError();
changePushState(false);
}
@Override
public void onConnectionLost() {
super.onConnectionLost();
runOnUiThread(() -> mConnectionLostTipsView.show());
}
@Override
public void onReceiveSEIMessage(int payload, byte[] data) {
super.onReceiveSEIMessage(payload, data);
}
@Override
public void onPlayerSei(int payload, byte[] uuid, byte[] data) {
super.onPlayerSei(payload, uuid, data);
}
});
//开始连麦
mInteractiveConnectView.setConnectClickListener(() -> {
if (mBareStreamController.isPulling()) {
//主播端停止连麦
mCurrentIntent = InteractLiveIntent.INTENT_STOP_PULL;
showInteractLiveDialog(getResources().getString(R.string.interact_live_connect_finish_tips), false);
} else {
//主播端开始连麦,输入用户 id
if (mPullUserData == null || TextUtils.isEmpty(mPullUserData.url)) {
showInteractLiveDialog(getResources().getString(R.string.interact_live_url_connect_tips), true);
return;
}
mBareStreamController.startPull(mPullUserData);
}
});
mCloseImageView.setOnClickListener(view -> {
mCurrentIntent = InteractLiveIntent.INTENT_FINISH;
showInteractLiveDialog(getResources().getString(R.string.interact_live_leave_room_tips), false);
});
mInteractiveRoomControlView.setOnClickEventListener(new InteractiveRoomControlView.OnClickEventListener() {
@Override
public void onClickSwitchCamera() {
mBareStreamController.switchCamera();
}
@Override
public void onClickSpeakerPhone(boolean enable) {
mBareStreamController.enableSpeakerPhone(enable);
}
@Override
public void onClickMuteAudio(boolean mute) {
mBareStreamController.setMute(mute);
}
@Override
public void onClickMuteVideo(boolean mute) {
mBareStreamController.setMute(mute);
}
@Override
public void onClickEnableAudio(boolean enable) {
mBareStreamController.enableAudioCapture(enable);
}
@Override
public void onClickEnableVideo(boolean enable) {
mBareStreamController.enableLocalCamera(enable);
}
});
}
private void changeSmallSurfaceViewVisible(boolean isShowSurfaceView) {
mSmallFrameLayout.setVisibility(isShowSurfaceView ? View.VISIBLE : View.INVISIBLE);
mInteractiveConnectView.isShow(isShowSurfaceView);
}
public void updateConnectTextView(boolean connecting) {
if (connecting) {
mShowConnectIdTextView.setVisibility(View.VISIBLE);
mInteractiveConnectView.connected(getResources().getString(R.string.interactive_bare_stream_stop_pull));
} else {
mShowConnectIdTextView.setVisibility(View.GONE);
mInteractiveConnectView.unConnected(getResources().getString(R.string.interactive_bare_stream_start_pull));
}
}
private void showInteractLiveDialog(String content, boolean showInputView) {
commonInputView = new InteractiveCommonInputView(InteractiveBareActivity.this);
commonInputView.setViewType(InteractiveCommonInputView.ViewType.BARE_STREAM);
commonInputView.showInputView(content, showInputView);
mAUILiveDialog.setContentView(commonInputView);
mAUILiveDialog.show();
commonInputView.setOnInteractLiveTipsViewListener(new InteractLiveTipsViewListener() {
@Override
public void onCancel() {
if (mAUILiveDialog.isShowing()) {
mAUILiveDialog.dismiss();
}
}
@Override
public void onConfirm() {
if (mCurrentIntent == InteractLiveIntent.INTENT_STOP_PULL) {
//主播结束连麦
mAUILiveDialog.dismiss();
mBareStreamController.stopPull();
updateConnectTextView(false);
changeSmallSurfaceViewVisible(false);
} else if (mCurrentIntent == InteractLiveIntent.INTENT_FINISH) {
finish();
}
}
@Override
public void onInputConfirm(InteractiveUserData userData) {
hideInputSoftFromWindowMethod(InteractiveBareActivity.this, commonInputView);
if (userData == null || TextUtils.isEmpty(userData.url)) {
ToastUtils.show(getResources().getString(R.string.interact_live_connect_input_error_tips));
return;
}
mAUILiveDialog.dismiss();
// 主播端,输入观众 id 后,开始连麦
mPullUserData = userData;
mBareStreamController.startPull(userData);
}
@Override
public void onQrClick() {
startQr(REQ_CODE_PULL_URL);
}
});
}
@Override
protected void onResume() {
super.onResume();
}
@Override
protected void onPause() {
super.onPause();
}
@Override
protected void onDestroy() {
super.onDestroy();
mBareStreamController.release();
}
public void hideInputSoftFromWindowMethod(Context context, View view) {
try {
InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case REQ_CODE_PUSH_URL:
case REQ_CODE_PULL_URL:
switch (resultCode) {
case RESULT_OK:
if (requestCode == REQ_CODE_PUSH_URL) {
if (mInteractivePushUrlEditText != null) {
mInteractivePushUrlEditText.setText(data.getStringExtra(CaptureActivity.EXTRA_SCAN_RESULT));
}
} else {
if (commonInputView != null) {
commonInputView.setQrResult(data.getStringExtra(CaptureActivity.EXTRA_SCAN_RESULT));
}
}
break;
case RESULT_CANCELED:
if (data != null) {
// for some reason camera is not working correctly
ToastUtils.show(getString(R.string.live_push_switch_to_facing_back_camera));
}
break;
default:
break;
}
break;
default:
break;
}
}
private void changePushState(boolean isSuccess) {
this.mIsPushing = isSuccess;
if (isSuccess) {
mStartPushTextView.setText(getResources().getString(com.alivc.live.interactive_common.R.string.interact_stop_push));
mStartPushTextView.setBackground(getResources().getDrawable(com.alivc.live.interactive_common.R.drawable.shape_interact_live_un_connect_btn_bg));
} else {
mStartPushTextView.setText(getResources().getString(com.alivc.live.interactive_common.R.string.interact_start_push));
mStartPushTextView.setBackground(getResources().getDrawable(com.alivc.live.interactive_common.R.drawable.shape_pysh_btn_bg));
}
}
private void startQr(int requestCode) {
if (ContextCompat.checkSelfPermission(InteractiveBareActivity.this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
// Do not have the permission of camera, request it.
ActivityCompat.requestPermissions(InteractiveBareActivity.this, new String[]{Manifest.permission.CAMERA}, REQ_CODE_PERMISSION);
} else {
// Have gotten the permission
startCaptureActivityForResult(requestCode);
}
}
private void startCaptureActivityForResult(int requestCode) {
Intent intent = new Intent(InteractiveBareActivity.this, CaptureActivity.class);
Bundle bundle = new Bundle();
bundle.putBoolean(CaptureActivity.KEY_NEED_BEEP, CaptureActivity.VALUE_BEEP);
bundle.putBoolean(CaptureActivity.KEY_NEED_VIBRATION, CaptureActivity.VALUE_VIBRATION);
bundle.putBoolean(CaptureActivity.KEY_NEED_EXPOSURE, CaptureActivity.VALUE_NO_EXPOSURE);
bundle.putByte(CaptureActivity.KEY_FLASHLIGHT_MODE, CaptureActivity.VALUE_FLASHLIGHT_OFF);
bundle.putByte(CaptureActivity.KEY_ORIENTATION_MODE, CaptureActivity.VALUE_ORIENTATION_AUTO);
bundle.putBoolean(CaptureActivity.KEY_SCAN_AREA_FULL_SCREEN, CaptureActivity.VALUE_SCAN_AREA_FULL_SCREEN);
bundle.putBoolean(CaptureActivity.KEY_NEED_SCAN_HINT_TEXT, CaptureActivity.VALUE_SCAN_HINT_TEXT);
intent.putExtra(CaptureActivity.EXTRA_SETTING_BUNDLE, bundle);
startActivityForResult(intent, requestCode);
}
}

View File

@@ -0,0 +1,196 @@
package com.alivc.live.barestream_interactive;
import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Color;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import com.acker.simplezxing.activity.CaptureActivity;
import com.alivc.live.commonui.utils.StatusBarUtil;
import com.alivc.live.interactive_common.InteractiveSettingActivity;
import com.alivc.live.interactive_common.utils.LivePushGlobalConfig;
import com.alivc.live.interactive_common.widget.InteractiveQrEditView;
import java.util.HashMap;
public class InteractiveInputURLActivity extends AppCompatActivity {
private static final int REQ_CODE_PERMISSION = 0x1111;
private static final int REQ_CODE_PUSH_URL = 0x2222;
private static final int REQ_CODE_PULL_URL = 0x3333;
private static final int REQ_CODE_PULL_URL_1 = 0x4444;
private static final int REQ_CODE_PULL_URL_2 = 0x5555;
private static final int REQ_CODE_PULL_URL_3 = 0x6666;
private TextView mConfirmTextView;
private TextView mSettingTextView;
private ImageView mBackImageView;
private InteractiveQrEditView mInteractivePushView;
private InteractiveQrEditView mInteractivePullView;
private InteractiveQrEditView mInteractivePullView1;
private InteractiveQrEditView mInteractivePullView2;
private InteractiveQrEditView mInteractivePullView3;
private LinearLayout mMorePullViewRoot;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
StatusBarUtil.translucent(this, Color.TRANSPARENT);
setContentView(R.layout.activity_interactive_input_url);
initView();
initListener();
}
private void initView() {
mBackImageView = findViewById(R.id.iv_back);
mConfirmTextView = findViewById(R.id.tv_confirm);
mSettingTextView = findViewById(R.id.tv_setting);
mMorePullViewRoot = findViewById(R.id.more_pull_view);
mInteractivePushView = findViewById(R.id.push_view);
mInteractivePullView = findViewById(R.id.pull_view);
mInteractivePullView1 = findViewById(R.id.pull_view1);
mInteractivePullView2 = findViewById(R.id.pull_view2);
mInteractivePullView3 = findViewById(R.id.pull_view3);
}
private void initListener() {
mInteractivePushView.setOnQrClickListener(() -> {
startQr(REQ_CODE_PUSH_URL);
});
mInteractivePullView.setOnQrClickListener(() -> {
startQr(REQ_CODE_PULL_URL);
});
mInteractivePullView1.setOnQrClickListener(() -> {
startQr(REQ_CODE_PULL_URL_1);
});
mInteractivePullView2.setOnQrClickListener(() -> {
startQr(REQ_CODE_PULL_URL_2);
});
mInteractivePullView3.setOnQrClickListener(() -> {
startQr(REQ_CODE_PULL_URL_3);
});
mBackImageView.setOnClickListener(view -> finish());
mConfirmTextView.setOnClickListener(view -> {
HashMap<String, String> extras = new HashMap<>();
String pushUrl = mInteractivePushView.getEditText();
String pullUrl = mInteractivePullView.getEditText();
extras.put(InteractiveBareActivity.DATA_INTERACTIVE_PUSH_URL, pushUrl);
extras.put(InteractiveBareActivity.DATA_INTERACTIVE_PULL_URL, pullUrl);
Intent intent;
if (LivePushGlobalConfig.IS_MULTI_INTERACT) {
intent = new Intent(InteractiveInputURLActivity.this, InteractiveMultiBareActivity.class);
String pullUrl1 = mInteractivePullView1.getEditText();
String pullUrl2 = mInteractivePullView2.getEditText();
String pullUrl3 = mInteractivePullView3.getEditText();
extras.put(InteractiveMultiBareActivity.DATA_INTERACTIVE_PULL_URL_1, pullUrl1);
extras.put(InteractiveMultiBareActivity.DATA_INTERACTIVE_PULL_URL_2, pullUrl2);
extras.put(InteractiveMultiBareActivity.DATA_INTERACTIVE_PULL_URL_3, pullUrl3);
} else {
intent = new Intent(InteractiveInputURLActivity.this, InteractiveBareActivity.class);
}
for (HashMap.Entry<String, String> entry : extras.entrySet()) {
intent.putExtra(entry.getKey(), entry.getValue());
}
startActivity(intent);
});
mSettingTextView.setOnClickListener(view -> {
Intent intent = new Intent(this, InteractiveSettingActivity.class);
startActivity(intent);
});
}
@Override
protected void onResume() {
super.onResume();
mMorePullViewRoot.setVisibility(LivePushGlobalConfig.IS_MULTI_INTERACT ? View.VISIBLE : View.GONE);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case REQ_CODE_PUSH_URL:
case REQ_CODE_PULL_URL:
case REQ_CODE_PULL_URL_1:
case REQ_CODE_PULL_URL_2:
case REQ_CODE_PULL_URL_3:
switch (resultCode) {
case RESULT_OK:
if (requestCode == REQ_CODE_PUSH_URL) {
if (mInteractivePushView != null) {
mInteractivePushView.setEditText(data.getStringExtra(CaptureActivity.EXTRA_SCAN_RESULT));
}
} else if (requestCode == REQ_CODE_PULL_URL) {
if (mInteractivePullView != null) {
mInteractivePullView.setEditText(data.getStringExtra(CaptureActivity.EXTRA_SCAN_RESULT));
}
} else if (requestCode == REQ_CODE_PULL_URL_1) {
if (mInteractivePullView1 != null) {
mInteractivePullView1.setEditText(data.getStringExtra(CaptureActivity.EXTRA_SCAN_RESULT));
}
} else if (requestCode == REQ_CODE_PULL_URL_2) {
if (mInteractivePullView2 != null) {
mInteractivePullView2.setEditText(data.getStringExtra(CaptureActivity.EXTRA_SCAN_RESULT));
}
} else {
if (mInteractivePullView3 != null) {
mInteractivePullView3.setEditText(data.getStringExtra(CaptureActivity.EXTRA_SCAN_RESULT));
}
}
break;
default:
break;
}
break;
default:
break;
}
}
private void startQr(int requestCode) {
if (ContextCompat.checkSelfPermission(InteractiveInputURLActivity.this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
// Do not have the permission of camera, request it.
ActivityCompat.requestPermissions(InteractiveInputURLActivity.this, new String[]{Manifest.permission.CAMERA}, REQ_CODE_PERMISSION);
} else {
// Have gotten the permission
startCaptureActivityForResult(requestCode);
}
}
private void startCaptureActivityForResult(int requestCode) {
Intent intent = new Intent(InteractiveInputURLActivity.this, CaptureActivity.class);
Bundle bundle = new Bundle();
bundle.putBoolean(CaptureActivity.KEY_NEED_BEEP, CaptureActivity.VALUE_BEEP);
bundle.putBoolean(CaptureActivity.KEY_NEED_VIBRATION, CaptureActivity.VALUE_VIBRATION);
bundle.putBoolean(CaptureActivity.KEY_NEED_EXPOSURE, CaptureActivity.VALUE_NO_EXPOSURE);
bundle.putByte(CaptureActivity.KEY_FLASHLIGHT_MODE, CaptureActivity.VALUE_FLASHLIGHT_OFF);
bundle.putByte(CaptureActivity.KEY_ORIENTATION_MODE, CaptureActivity.VALUE_ORIENTATION_AUTO);
bundle.putBoolean(CaptureActivity.KEY_SCAN_AREA_FULL_SCREEN, CaptureActivity.VALUE_SCAN_AREA_FULL_SCREEN);
bundle.putBoolean(CaptureActivity.KEY_NEED_SCAN_HINT_TEXT, CaptureActivity.VALUE_SCAN_HINT_TEXT);
intent.putExtra(CaptureActivity.EXTRA_SETTING_BUNDLE, bundle);
startActivityForResult(intent, requestCode);
}
}

View File

@@ -0,0 +1,460 @@
package com.alivc.live.barestream_interactive;
import android.Manifest;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Color;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.acker.simplezxing.activity.CaptureActivity;
import com.alivc.live.barestream_interactive.adapter.InteractiveMultiBareRecyclerViewAdapter;
import com.alivc.live.barestream_interactive.bean.BareStreamBean;
import com.alivc.live.commonui.utils.StatusBarUtil;
import com.alivc.live.commonutils.ToastUtils;
import com.alivc.live.interactive_common.bean.InteractiveUserData;
import com.alivc.live.interactive_common.listener.InteractLiveTipsViewListener;
import com.alivc.live.interactive_common.listener.InteractLivePushPullListener;
import com.alivc.live.interactive_common.utils.InteractLiveIntent;
import com.alivc.live.commonui.avdialog.AUILiveDialog;
import com.alivc.live.interactive_common.widget.ConnectionLostTipsView;
import com.alivc.live.interactive_common.widget.InteractiveCommonInputView;
import com.alivc.live.interactive_common.widget.InteractiveRoomControlView;
import com.alivc.live.player.annotations.AlivcLivePlayError;
import java.util.ArrayList;
import java.util.List;
public class InteractiveMultiBareActivity extends AppCompatActivity {
private static final int REQ_CODE_PERMISSION = 0x1111;
private static final int REQ_CODE_PUSH_URL = 0x2222;
private static final int REQ_CODE_PULL_URL = 0x3333;
public static final String DATA_INTERACTIVE_PUSH_URL = "data_interactive_push_url";
public static final String DATA_INTERACTIVE_PULL_URL = "data_interactive_pull_url";
public static final String DATA_INTERACTIVE_PULL_URL_1 = "data_interactive_pull_url_1";
public static final String DATA_INTERACTIVE_PULL_URL_2 = "data_interactive_pull_url_2";
public static final String DATA_INTERACTIVE_PULL_URL_3 = "data_interactive_pull_url_3";
private AUILiveDialog mAUILiveDialog;
//Dialog 弹窗的意图
private InteractLiveIntent mCurrentIntent;
private ImageView mCloseImageView;
private TextView mShowConnectIdTextView;
//大窗口
private FrameLayout mBigFrameLayout;
//小窗口
private ConnectionLostTipsView mConnectionLostTipsView;
//输入 URL 方式连麦
private InteractiveUserData mPushUserData;
private InteractiveUserData mPullUserData;
private InteractiveUserData mPullUserData1;
private InteractiveUserData mPullUserData2;
private InteractiveUserData mPullUserData3;
private InteractiveCommonInputView commonInputView;
private InteractiveRoomControlView mInteractiveRoomControlView;
private MultiBareStreamController mBareStreamController;
private EditText mInteractivePushUrlEditText;
private ImageView mPushUrlQrImageView;
private TextView mStartPushTextView;
private InteractiveMultiBareRecyclerViewAdapter mAdapter;
private final List<BareStreamBean> mFakeList = new ArrayList<>();
private RecyclerView mRecyclerView;
private BareStreamBean mCurrentBareStreamBean;
private int mCurrentCheckedPosition;
private boolean mIsPushing;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
StatusBarUtil.translucent(this, Color.TRANSPARENT);
setContentView(R.layout.activity_interactive_multi_bare);
InteractiveUserData pushUserData = new InteractiveUserData();
pushUserData.url = getIntent().getStringExtra(DATA_INTERACTIVE_PUSH_URL);
mPushUserData = pushUserData;
InteractiveUserData pullUserData = new InteractiveUserData();
pullUserData.url = getIntent().getStringExtra(DATA_INTERACTIVE_PULL_URL);
mPullUserData = pullUserData;
InteractiveUserData pullUserData1 = new InteractiveUserData();
pullUserData1.url = getIntent().getStringExtra(DATA_INTERACTIVE_PULL_URL_1);
mPullUserData1 = pullUserData1;
InteractiveUserData pullUserData2 = new InteractiveUserData();
pullUserData2.url = getIntent().getStringExtra(DATA_INTERACTIVE_PULL_URL_2);
mPullUserData2 = pullUserData2;
InteractiveUserData pullUserData3 = new InteractiveUserData();
pullUserData3.url = getIntent().getStringExtra(DATA_INTERACTIVE_PULL_URL_3);
mPullUserData3 = pullUserData3;
mBareStreamController = new MultiBareStreamController(this);
initView();
initListener();
initData();
}
private void initData() {
mInteractivePushUrlEditText.setText(mPushUserData != null ? mPushUserData.url : "");
mBareStreamController.setAnchorRenderView(mBigFrameLayout);
for (int i = 0; i < 4; i++) {
BareStreamBean bareStreamBean = new BareStreamBean();
switch (i) {
case 0:
bareStreamBean.setUserData(mPullUserData);
break;
case 1:
bareStreamBean.setUserData(mPullUserData1);
break;
case 2:
bareStreamBean.setUserData(mPullUserData2);
break;
case 3:
bareStreamBean.setUserData(mPullUserData3);
break;
}
mFakeList.add(bareStreamBean);
}
mAdapter.setData(mFakeList);
}
private void initView() {
mAUILiveDialog = new AUILiveDialog(this);
mCloseImageView = findViewById(R.id.iv_close);
mShowConnectIdTextView = findViewById(R.id.tv_show_connect);
mBigFrameLayout = findViewById(R.id.big_fl);
mInteractiveRoomControlView = findViewById(R.id.interactive_setting_view);
mConnectionLostTipsView = new ConnectionLostTipsView(this);
mPushUrlQrImageView = findViewById(R.id.iv_qr_push_url);
mStartPushTextView = findViewById(R.id.tv_start_push);
mInteractivePushUrlEditText = findViewById(R.id.et_interactive_push_url);
mRecyclerView = findViewById(R.id.recycler_view);
GridLayoutManager gridLayoutManager = new GridLayoutManager(this, 2);
gridLayoutManager.setOrientation(GridLayoutManager.HORIZONTAL);
mRecyclerView.setLayoutManager(gridLayoutManager);
mAdapter = new InteractiveMultiBareRecyclerViewAdapter();
mRecyclerView.setAdapter(mAdapter);
}
private void initListener() {
mPushUrlQrImageView.setOnClickListener(view -> {
startQr(REQ_CODE_PUSH_URL);
});
mAdapter.setOnConnectClickListener(position -> {
mCurrentCheckedPosition = position;
mCurrentBareStreamBean = mFakeList.get(position);
if (mCurrentBareStreamBean.isConnected()) {
mCurrentIntent = InteractLiveIntent.INTENT_STOP_PULL;
showInteractLiveDialog(getResources().getString(R.string.interact_live_connect_finish_tips), false);
} else {
if (TextUtils.isEmpty(mCurrentBareStreamBean.getUrl())) {
showInteractLiveDialog(getResources().getString(R.string.interact_live_url_connect_tips), true);
} else {
FrameLayout renderViewByPosition = getRenderViewByPosition(position);
if (renderViewByPosition != null) {
mBareStreamController.startConnect(mCurrentBareStreamBean.getUserData(), renderViewByPosition);
}
}
}
});
mStartPushTextView.setOnClickListener(view -> {
if (mIsPushing) {
mBareStreamController.stopPush();
changePushState(false);
} else {
InteractiveUserData userData = new InteractiveUserData();
userData.url = mInteractivePushUrlEditText.getText().toString().trim();
mBareStreamController.setPushData(userData);
mBareStreamController.startPush();
}
});
mConnectionLostTipsView.setConnectionLostListener(() -> runOnUiThread(() -> finish()));
mBareStreamController.setMultiInteractLivePushPullListener(new InteractLivePushPullListener() {
@Override
public void onPushSuccess() {
super.onPushSuccess();
changePushState(true);
}
@Override
public void onPushError() {
super.onPushError();
changePushState(false);
}
@Override
public void onPullSuccess(InteractiveUserData userData) {
BareStreamBean bean = mFakeList.get(Integer.parseInt(userData.getKey()));
if (bean != null) {
bean.setConnected(true);
}
mAdapter.notifyDataSetChanged();
}
@Override
public void onPullError(InteractiveUserData userData, AlivcLivePlayError errorType, String errorMsg) {
super.onPullError(userData, errorType, errorMsg);
runOnUiThread(() -> {
BareStreamBean bean = mFakeList.get(Integer.parseInt(userData.getKey()));
if (bean != null) {
bean.setConnected(false);
}
mAdapter.notifyDataSetChanged();
mBareStreamController.stopConnect(userData);
updateConnectTextView(false);
ToastUtils.show(getResources().getString(R.string.interact_live_viewer_left));
});
}
@Override
public void onPullStop(InteractiveUserData userData) {
runOnUiThread(() -> {
updateConnectTextView(false);
});
}
@Override
public void onConnectionLost() {
runOnUiThread(() -> mConnectionLostTipsView.show());
}
});
mCloseImageView.setOnClickListener(view -> {
mCurrentIntent = InteractLiveIntent.INTENT_FINISH;
showInteractLiveDialog(getResources().getString(R.string.interact_live_leave_room_tips), false);
});
mInteractiveRoomControlView.setOnClickEventListener(new InteractiveRoomControlView.OnClickEventListener() {
@Override
public void onClickSwitchCamera() {
mBareStreamController.switchCamera();
}
@Override
public void onClickSpeakerPhone(boolean enable) {
mBareStreamController.enableSpeakerPhone(enable);
}
@Override
public void onClickMuteAudio(boolean mute) {
mBareStreamController.setMute(mute);
}
@Override
public void onClickMuteVideo(boolean mute) {
mBareStreamController.muteLocalCamera(mute);
}
@Override
public void onClickEnableAudio(boolean enable) {
mBareStreamController.enableAudioCapture(enable);
}
@Override
public void onClickEnableVideo(boolean enable) {
mBareStreamController.enableLocalCamera(enable);
}
});
}
public void updateConnectTextView(boolean connecting) {
mShowConnectIdTextView.setVisibility(connecting ? View.VISIBLE : View.GONE);
}
private void showInteractLiveDialog(String content, boolean showInputView) {
commonInputView = new InteractiveCommonInputView(InteractiveMultiBareActivity.this);
commonInputView.setViewType(InteractiveCommonInputView.ViewType.BARE_STREAM);
commonInputView.showInputView(content, showInputView);
mAUILiveDialog.setContentView(commonInputView);
mAUILiveDialog.show();
commonInputView.setOnInteractLiveTipsViewListener(new InteractLiveTipsViewListener() {
@Override
public void onCancel() {
if (mAUILiveDialog.isShowing()) {
mAUILiveDialog.dismiss();
}
}
@Override
public void onConfirm() {
if (mCurrentIntent == InteractLiveIntent.INTENT_STOP_PULL) {
//主播结束连麦
mAUILiveDialog.dismiss();
mBareStreamController.stopConnect(mCurrentBareStreamBean.getUserData());
updateConnectTextView(false);
mCurrentBareStreamBean.setConnected(false);
mAdapter.notifyDataSetChanged();
} else if (mCurrentIntent == InteractLiveIntent.INTENT_FINISH) {
finish();
}
}
@Override
public void onInputConfirm(InteractiveUserData userData) {
hideInputSoftFromWindowMethod(InteractiveMultiBareActivity.this, commonInputView);
if (userData == null || TextUtils.isEmpty(userData.url)) {
ToastUtils.show(getResources().getString(R.string.interact_live_connect_input_error_tips));
return;
}
mAUILiveDialog.dismiss();
//主播端,输入观众 id 后,开始连麦
mCurrentBareStreamBean.setUserData(userData);
FrameLayout renderViewByPosition = getRenderViewByPosition(mCurrentCheckedPosition);
mBareStreamController.startConnect(mCurrentBareStreamBean.getUserData(), renderViewByPosition);
}
@Override
public void onQrClick() {
startQr(REQ_CODE_PULL_URL);
}
});
}
@Override
protected void onResume() {
super.onResume();
}
@Override
protected void onPause() {
super.onPause();
}
@Override
protected void onDestroy() {
super.onDestroy();
mBareStreamController.release();
}
public void hideInputSoftFromWindowMethod(Context context, View view) {
try {
InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case REQ_CODE_PUSH_URL:
case REQ_CODE_PULL_URL:
switch (resultCode) {
case RESULT_OK:
if (requestCode == REQ_CODE_PUSH_URL) {
if (mInteractivePushUrlEditText != null) {
mInteractivePushUrlEditText.setText(data.getStringExtra(CaptureActivity.EXTRA_SCAN_RESULT));
}
} else {
if (commonInputView != null) {
commonInputView.setQrResult(data.getStringExtra(CaptureActivity.EXTRA_SCAN_RESULT));
}
}
break;
case RESULT_CANCELED:
if (data != null) {
// for some reason camera is not working correctly
ToastUtils.show(getString(R.string.live_push_switch_to_facing_back_camera));
}
break;
default:
break;
}
break;
default:
break;
}
}
private void changePushState(boolean isSuccess) {
this.mIsPushing = isSuccess;
if (isSuccess) {
mStartPushTextView.setText(getResources().getString(com.alivc.live.interactive_common.R.string.interact_stop_push));
mStartPushTextView.setBackground(getResources().getDrawable(com.alivc.live.interactive_common.R.drawable.shape_interact_live_un_connect_btn_bg));
} else {
mStartPushTextView.setText(getResources().getString(com.alivc.live.interactive_common.R.string.interact_start_push));
mStartPushTextView.setBackground(getResources().getDrawable(com.alivc.live.interactive_common.R.drawable.shape_pysh_btn_bg));
}
}
private InteractiveMultiBareRecyclerViewAdapter.InteractiveMultiBareViewHolder getViewHolderByPosition(int position) {
return (InteractiveMultiBareRecyclerViewAdapter.InteractiveMultiBareViewHolder) mRecyclerView.findViewHolderForAdapterPosition(position);
}
private FrameLayout getRenderViewByPosition(int position) {
InteractiveMultiBareRecyclerViewAdapter.InteractiveMultiBareViewHolder viewHolder = getViewHolderByPosition(position);
if (viewHolder != null) {
return viewHolder.getRenderFrameLayout();
} else {
return null;
}
}
private int getAdapterPositionFromByKey(String key) {
for (int i = 0; i < mFakeList.size(); i++) {
if (key.equals(mFakeList.get(i).getUrl())) {
return i;
}
}
return -1;
}
private void startQr(int requestCode) {
if (ContextCompat.checkSelfPermission(InteractiveMultiBareActivity.this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
// Do not have the permission of camera, request it.
ActivityCompat.requestPermissions(InteractiveMultiBareActivity.this, new String[]{Manifest.permission.CAMERA}, REQ_CODE_PERMISSION);
} else {
// Have gotten the permission
startCaptureActivityForResult(requestCode);
}
}
private void startCaptureActivityForResult(int requestCode) {
Intent intent = new Intent(InteractiveMultiBareActivity.this, CaptureActivity.class);
Bundle bundle = new Bundle();
bundle.putBoolean(CaptureActivity.KEY_NEED_BEEP, CaptureActivity.VALUE_BEEP);
bundle.putBoolean(CaptureActivity.KEY_NEED_VIBRATION, CaptureActivity.VALUE_VIBRATION);
bundle.putBoolean(CaptureActivity.KEY_NEED_EXPOSURE, CaptureActivity.VALUE_NO_EXPOSURE);
bundle.putByte(CaptureActivity.KEY_FLASHLIGHT_MODE, CaptureActivity.VALUE_FLASHLIGHT_OFF);
bundle.putByte(CaptureActivity.KEY_ORIENTATION_MODE, CaptureActivity.VALUE_ORIENTATION_AUTO);
bundle.putBoolean(CaptureActivity.KEY_SCAN_AREA_FULL_SCREEN, CaptureActivity.VALUE_SCAN_AREA_FULL_SCREEN);
bundle.putBoolean(CaptureActivity.KEY_NEED_SCAN_HINT_TEXT, CaptureActivity.VALUE_SCAN_HINT_TEXT);
intent.putExtra(CaptureActivity.EXTRA_SETTING_BUNDLE, bundle);
startActivityForResult(intent, requestCode);
}
}

View File

@@ -0,0 +1,132 @@
package com.alivc.live.barestream_interactive;
import android.content.Context;
import android.widget.FrameLayout;
import com.alivc.live.commonbiz.LocalStreamReader;
import com.alivc.live.commonbiz.ResourcesConst;
import com.alivc.live.interactive_common.InteractLiveBaseManager;
import com.alivc.live.interactive_common.InteractiveMode;
import com.alivc.live.interactive_common.bean.InteractiveUserData;
import com.alivc.live.interactive_common.listener.InteractLivePushPullListener;
import com.alivc.live.interactive_common.utils.LivePushGlobalConfig;
import com.alivc.live.pusher.AlivcResolutionEnum;
import java.io.File;
class MultiBareStreamController {
private final InteractLiveBaseManager mInteractLiveManager;
private final Context mContext;
private final LocalStreamReader mLocalStreamReader;
//主播预览 View
private FrameLayout mAnchorRenderView;
//主播推流地址
private InteractiveUserData mPushUserData;
public MultiBareStreamController(Context context) {
this.mContext = context;
AlivcResolutionEnum resolution = LivePushGlobalConfig.mAlivcLivePushConfig.getResolution();
int width = AlivcResolutionEnum.getResolutionWidth(resolution, LivePushGlobalConfig.mAlivcLivePushConfig.getLivePushMode());
int height = AlivcResolutionEnum.getResolutionHeight(resolution, LivePushGlobalConfig.mAlivcLivePushConfig.getLivePushMode());
mLocalStreamReader = new LocalStreamReader.Builder()
.setVideoWith(width)
.setVideoHeight(height)
.setVideoStride(width)
.setVideoSize(height * width * 3 / 2)
.setVideoRotation(0)
.setAudioSampleRate(44100)
.setAudioChannel(1)
.setAudioBufferSize(2048)
.build();
mInteractLiveManager = new InteractLiveBaseManager();
mInteractLiveManager.init(context, InteractiveMode.MULTI_BARE_STREAM);
}
public void setPushData(InteractiveUserData userData) {
mPushUserData = userData;
}
/**
* 设置主播预览 View
*
* @param frameLayout 主播预览 View
*/
public void setAnchorRenderView(FrameLayout frameLayout) {
this.mAnchorRenderView = frameLayout;
}
public void stopPush() {
mInteractLiveManager.stopPush();
}
/**
* 开始直播
*/
public void startPush() {
externAV();
mInteractLiveManager.startPreviewAndPush(mPushUserData, mAnchorRenderView, true);
}
private void externAV() {
if (LivePushGlobalConfig.mAlivcLivePushConfig.isExternMainStream()) {
File yuvFile = ResourcesConst.localCaptureYUVFilePath(mContext);
mLocalStreamReader.readYUVData(yuvFile, (buffer, pts, videoWidth, videoHeight, videoStride, videoSize, videoRotation) -> {
mInteractLiveManager.inputStreamVideoData(buffer, videoWidth, videoHeight, videoStride, videoSize, pts, videoRotation);
});
File pcmFile = ResourcesConst.localCapturePCMFilePath(mContext);
mLocalStreamReader.readPCMData(pcmFile, (buffer, length, pts, audioSampleRate, audioChannel) -> {
mInteractLiveManager.inputStreamAudioData(buffer, length, audioSampleRate, audioChannel, pts);
});
}
}
/**
* 开始连麦
*/
public void startConnect(InteractiveUserData userData, FrameLayout mRenderView) {
mInteractLiveManager.setPullView(userData, mRenderView, false);
mInteractLiveManager.startPullRTCStream(userData);
}
/**
* 结束连麦
*/
public void stopConnect(InteractiveUserData userData) {
mInteractLiveManager.stopPullRTCStream(userData);
}
public void release() {
mInteractLiveManager.release();
mLocalStreamReader.stopYUV();
mLocalStreamReader.stopPcm();
}
public void setMultiInteractLivePushPullListener(InteractLivePushPullListener listener) {
mInteractLiveManager.setInteractLivePushPullListener(listener);
}
public void switchCamera() {
mInteractLiveManager.switchCamera();
}
public void enableSpeakerPhone(boolean enable) {
mInteractLiveManager.enableSpeakerPhone(enable);
}
public void setMute(boolean b) {
mInteractLiveManager.setMute(b);
}
public void enableAudioCapture(boolean enable) {
mInteractLiveManager.enableAudioCapture(enable);
}
public void muteLocalCamera(boolean muteLocalCamera) {
mInteractLiveManager.muteLocalCamera(muteLocalCamera);
}
public void enableLocalCamera(boolean enable) {
mInteractLiveManager.enableLocalCamera(enable);
}
}

View File

@@ -0,0 +1,130 @@
package com.alivc.live.barestream_interactive.adapter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.alivc.live.barestream_interactive.R;
import com.alivc.live.barestream_interactive.bean.BareStreamBean;
import com.alivc.live.interactive_common.widget.InteractiveConnectView;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class InteractiveMultiBareRecyclerViewAdapter extends RecyclerView.Adapter<InteractiveMultiBareRecyclerViewAdapter.InteractiveMultiBareViewHolder> {
private Map<Integer, Integer> mFLayoutWithPositionMap = new HashMap<>();
private OnItemClickListener mListener;
private List<BareStreamBean> mData;
private OnItemViewChangedListener mOnPKItemViewChangedListener;
public boolean reSetFrameLayout(int position, int frameLayoutHashCode) {
if (mFLayoutWithPositionMap.containsKey(position)) {
if (frameLayoutHashCode == mFLayoutWithPositionMap.get(position)) {
return false;
} else {
mFLayoutWithPositionMap.put(position, frameLayoutHashCode);
return true;
}
}
return false;
}
@Override
public void onViewAttachedToWindow(InteractiveMultiBareViewHolder holder) {
super.onViewAttachedToWindow(holder);
if (mOnPKItemViewChangedListener != null) {
mOnPKItemViewChangedListener.onItemViewAttachedToWindow(holder.getAdapterPosition());
}
}
@Override
public void onViewDetachedFromWindow(InteractiveMultiBareViewHolder holder) {
super.onViewDetachedFromWindow(holder);
if (mOnPKItemViewChangedListener != null) {
mOnPKItemViewChangedListener.onItemViewDetachedToWindow(holder.getAdapterPosition());
}
}
@NonNull
@Override
public InteractiveMultiBareViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_multi_interactive_bare_item, parent, false);
return new InteractiveMultiBareViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull InteractiveMultiBareViewHolder holder, int position) {
BareStreamBean bareStreamBean = mData.get(position);
holder.isConnected(bareStreamBean.isConnected());
}
@Override
public int getItemCount() {
return mData == null ? 0 : mData.size();
}
public void setOnConnectClickListener(OnItemClickListener listener) {
this.mListener = listener;
}
public void setOnItemViewChangedListener(OnItemViewChangedListener listener) {
this.mOnPKItemViewChangedListener = listener;
}
public void setData(List<BareStreamBean> dataList) {
this.mData = dataList;
notifyDataSetChanged();
}
public class InteractiveMultiBareViewHolder extends RecyclerView.ViewHolder {
private final InteractiveConnectView mConnectView;
private final FrameLayout mRenderFrameLayout;
public InteractiveMultiBareViewHolder(@NonNull View itemView) {
super(itemView);
mConnectView = itemView.findViewById(R.id.connect_view);
mRenderFrameLayout = itemView.findViewById(R.id.render_frame);
mConnectView.setDefaultText(mConnectView.getResources().getString(R.string.interactive_bare_stream_start_pull));
mConnectView.setConnectClickListener(() -> {
if (mListener != null) {
mListener.onConnectClick(getAdapterPosition());
}
});
}
public FrameLayout getRenderFrameLayout() {
return mRenderFrameLayout;
}
public InteractiveConnectView getConnectView() {
return mConnectView;
}
public void isConnected(boolean isConnected) {
if (isConnected) {
mConnectView.connected(mConnectView.getResources().getString(R.string.interactive_bare_stream_stop_pull));
} else {
mConnectView.unConnected(mConnectView.getResources().getString(R.string.interactive_bare_stream_start_pull));
}
mConnectView.getConnectFrameLayout().setVisibility(isConnected ? View.INVISIBLE : View.VISIBLE);
}
}
public interface OnItemClickListener {
void onConnectClick(int position);
}
public interface OnItemViewChangedListener {
void onItemViewAttachedToWindow(int position);
void onItemViewDetachedToWindow(int position);
}
}

View File

@@ -0,0 +1,29 @@
package com.alivc.live.barestream_interactive.bean;
import com.alivc.live.interactive_common.bean.InteractiveUserData;
public class BareStreamBean {
private InteractiveUserData userData;
private boolean isConnected;
public InteractiveUserData getUserData() {
return userData;
}
public void setUserData(InteractiveUserData userData) {
this.userData = userData;
}
public String getUrl() {
return userData != null ? userData.url : null;
}
public boolean isConnected() {
return isConnected;
}
public void setConnected(boolean connected) {
isConnected = connected;
}
}

View File

@@ -0,0 +1,127 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="@+id/big_fl"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/wheel_black">
<FrameLayout
android:id="@+id/small_fl"
android:layout_width="90dp"
android:layout_height="160dp"
android:layout_gravity="right|bottom"
android:layout_marginEnd="33dp"
android:layout_marginBottom="110dp" />
</FrameLayout>
<ImageView
android:id="@+id/iv_close"
android:layout_width="26dp"
android:layout_height="26dp"
android:layout_marginStart="20dp"
android:src="@drawable/ic_close_white"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/tv_home_id" />
<TextView
android:id="@+id/tv_home_id"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="44dp"
android:textColor="#FCFCFD"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_show_connect"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/interact_live_connecting"
android:textColor="#FCFCFD"
android:textSize="12sp"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="@id/tv_home_id"
app:layout_constraintStart_toStartOf="@id/tv_home_id"
app:layout_constraintTop_toBottomOf="@id/tv_home_id" />
<TextView
android:id="@+id/tv_interactive_push_url"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="22dp"
android:layout_marginStart="20dp"
android:text="@string/interact_live_push_url"
android:gravity="center"
android:textColor="#FCFCFD"
android:textSize="16sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_show_connect"/>
<EditText
android:id="@+id/et_interactive_push_url"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:maxLines="1"
android:singleLine="true"
android:textColor="#E6E7EC"
android:textColorHint="#E6E7EC"
android:theme="@style/AUIEditText"
app:layout_constraintEnd_toStartOf="@id/tv_start_push"
app:layout_constraintStart_toStartOf="@id/tv_interactive_push_url"
app:layout_constraintTop_toBottomOf="@id/tv_interactive_push_url" />
<ImageView
android:id="@+id/iv_qr_push_url"
android:layout_width="24dp"
android:layout_height="24dp"
android:background="@color/transparent"
android:layout_margin="5dp"
android:src="@drawable/ic_scan_icon"
app:layout_constraintBottom_toBottomOf="@id/et_interactive_push_url"
app:layout_constraintEnd_toEndOf="@id/et_interactive_push_url"
app:layout_constraintTop_toTopOf="@id/et_interactive_push_url" />
<TextView
android:id="@+id/tv_start_push"
android:layout_width="94dp"
android:layout_height="30dp"
android:background="@drawable/shape_pysh_btn_bg"
android:ellipsize="end"
android:gravity="center"
android:includeFontPadding="false"
android:maxLines="1"
android:text="@string/interact_start_push"
android:textColor="@android:color/white"
android:textSize="16sp"
app:layout_constraintTop_toTopOf="@id/et_interactive_push_url"
app:layout_constraintBottom_toBottomOf="@id/et_interactive_push_url"
app:layout_constraintEnd_toEndOf="parent"/>
<com.alivc.live.interactive_common.widget.InteractiveRoomControlView
android:id="@+id/interactive_setting_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="20dp"
android:layout_marginTop="20dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/et_interactive_push_url"/>
<com.alivc.live.interactive_common.widget.InteractiveConnectView
android:id="@+id/connect_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="30dp"
android:layout_marginBottom="65dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,142 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#1C1D22">
<ImageView
android:id="@+id/iv_back"
android:layout_width="26dp"
android:layout_height="26dp"
android:layout_marginStart="20dp"
android:src="@drawable/ic_live_action_bar_back"
app:layout_constraintBottom_toBottomOf="@id/tv_title"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/tv_title" />
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="44dp"
android:layout_marginTop="44dp"
android:gravity="center"
android:text="@string/interact_live_url_title"
android:textColor="#FCFCFD"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_setting"
android:layout_width="wrap_content"
android:layout_height="44dp"
android:layout_marginTop="44dp"
android:layout_marginEnd="10dp"
android:gravity="center"
android:text="@string/interact_setting"
android:textColor="#FCFCFD"
android:textSize="12sp"
android:visibility="visible"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_confirm"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_gravity="bottom|center_horizontal"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp"
android:layout_marginBottom="44dp"
android:background="@drawable/shape_rect_blue"
android:clickable="false"
android:ellipsize="end"
android:gravity="center"
android:includeFontPadding="false"
android:maxLines="2"
android:text="@string/interact_live_confirm"
android:textColor="@android:color/white"
android:textSize="14sp"
app:layout_constraintBottom_toBottomOf="parent" />
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_title">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.alivc.live.interactive_common.widget.InteractiveQrEditView
android:id="@+id/push_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginTop="20dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:title="@string/interact_live_push_url" />
<com.alivc.live.interactive_common.widget.InteractiveQrEditView
android:id="@+id/pull_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginTop="20dp"
app:layout_constraintEnd_toEndOf="@id/push_view"
app:layout_constraintStart_toStartOf="@id/push_view"
app:layout_constraintTop_toBottomOf="@id/push_view"
app:title="@string/interact_live_pull_url" />
<LinearLayout
android:id="@+id/more_pull_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintTop_toBottomOf="@id/pull_view">
<com.alivc.live.interactive_common.widget.InteractiveQrEditView
android:id="@+id/pull_view1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginTop="20dp"
app:layout_constraintEnd_toEndOf="@id/push_view"
app:layout_constraintStart_toStartOf="@id/push_view"
app:layout_constraintTop_toBottomOf="@id/push_view"
app:title="@string/interact_live_pull_url" />
<com.alivc.live.interactive_common.widget.InteractiveQrEditView
android:id="@+id/pull_view2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginTop="20dp"
app:layout_constraintEnd_toEndOf="@id/push_view"
app:layout_constraintStart_toStartOf="@id/push_view"
app:layout_constraintTop_toBottomOf="@id/push_view"
app:title="@string/interact_live_pull_url" />
<com.alivc.live.interactive_common.widget.InteractiveQrEditView
android:id="@+id/pull_view3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginTop="20dp"
app:layout_constraintEnd_toEndOf="@id/push_view"
app:layout_constraintStart_toStartOf="@id/push_view"
app:layout_constraintTop_toBottomOf="@id/push_view"
app:title="@string/interact_live_pull_url" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,117 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="@+id/big_fl"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/wheel_black">
</FrameLayout>
<ImageView
android:id="@+id/iv_close"
android:layout_width="26dp"
android:layout_height="26dp"
android:layout_marginStart="20dp"
android:src="@drawable/ic_close_white"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/tv_home_id" />
<TextView
android:id="@+id/tv_home_id"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="44dp"
android:textColor="#FCFCFD"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_show_connect"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/interact_live_connecting"
android:textColor="#FCFCFD"
android:textSize="12sp"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="@id/tv_home_id"
app:layout_constraintStart_toStartOf="@id/tv_home_id"
app:layout_constraintTop_toBottomOf="@id/tv_home_id" />
<TextView
android:id="@+id/tv_interactive_push_url"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginTop="22dp"
android:gravity="center"
android:text="@string/interact_live_push_url"
android:textColor="#FCFCFD"
android:textSize="16sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_show_connect" />
<EditText
android:id="@+id/et_interactive_push_url"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:maxLines="1"
android:singleLine="true"
android:textColor="#E6E7EC"
android:textColorHint="#E6E7EC"
android:theme="@style/AUIEditText"
app:layout_constraintEnd_toStartOf="@id/tv_start_push"
app:layout_constraintStart_toStartOf="@id/tv_interactive_push_url"
app:layout_constraintTop_toBottomOf="@id/tv_interactive_push_url" />
<ImageView
android:id="@+id/iv_qr_push_url"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_margin="5dp"
android:background="@color/transparent"
android:src="@drawable/ic_scan_icon"
app:layout_constraintBottom_toBottomOf="@id/et_interactive_push_url"
app:layout_constraintEnd_toEndOf="@id/et_interactive_push_url"
app:layout_constraintTop_toTopOf="@id/et_interactive_push_url" />
<TextView
android:id="@+id/tv_start_push"
android:layout_width="94dp"
android:layout_height="30dp"
android:background="@drawable/shape_pysh_btn_bg"
android:ellipsize="end"
android:gravity="center"
android:includeFontPadding="false"
android:maxLines="1"
android:text="@string/interact_start_push"
android:textColor="@android:color/white"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="@id/et_interactive_push_url"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/et_interactive_push_url" />
<com.alivc.live.interactive_common.widget.InteractiveRoomControlView
android:id="@+id/interactive_setting_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:layout_marginEnd="20dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/et_interactive_push_url" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
xmlns:app="http://schemas.android.com/apk/res-auto">
<FrameLayout
android:id="@+id/render_frame"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintDimensionRatio="w,16:9"
android:background="@color/alivc_color_black"
app:layout_constraintStart_toStartOf="@id/connect_view"
app:layout_constraintEnd_toEndOf="@id/connect_view"
app:layout_constraintTop_toTopOf="parent">
</FrameLayout>
<com.alivc.live.interactive_common.widget.InteractiveConnectView
android:id="@+id/connect_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,4 @@
<resources>
<string name="interactive_bare_stream_start_pull">开始拉流</string>
<string name="interactive_bare_stream_stop_pull">结束拉流</string>
</resources>