Repository: AnyRTC/RTMPCHybridEngine-Android
Branch: github
Commit: b4403f3d2851
Files: 100
Total size: 271.6 KB
Directory structure:
gitextract_zej5ynzh/
├── .gitignore
├── README.md
├── app/
│ ├── .gitignore
│ ├── build.gradle
│ ├── gradle/
│ │ └── wrapper/
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
│ ├── gradlew
│ ├── gradlew.bat
│ ├── proguard-rules.pro
│ └── src/
│ ├── androidTest/
│ │ └── java/
│ │ └── org/
│ │ └── ar/
│ │ └── rtmpc/
│ │ └── ApplicationTest.java
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ │ └── org/
│ │ │ └── ar/
│ │ │ ├── ARApplication.java
│ │ │ ├── BaseActivity.java
│ │ │ ├── DeveloperInfo.java
│ │ │ ├── SplashActivity.java
│ │ │ ├── adapter/
│ │ │ │ ├── AudioLineAdapter.java
│ │ │ │ ├── LiveLineAdapter.java
│ │ │ │ ├── LiveListAdapter.java
│ │ │ │ ├── LiveMessageAdapter.java
│ │ │ │ └── LogAdapter.java
│ │ │ ├── guest/
│ │ │ │ ├── AudioGuestActivity.java
│ │ │ │ ├── GuestActivity.java
│ │ │ │ └── LiveListActivity.java
│ │ │ ├── hoster/
│ │ │ │ ├── AudioHosterActivity.java
│ │ │ │ ├── HosterActivity.java
│ │ │ │ └── LineFragment.java
│ │ │ ├── model/
│ │ │ │ ├── LineBean.java
│ │ │ │ ├── LiveBean.java
│ │ │ │ └── MessageBean.java
│ │ │ ├── utils/
│ │ │ │ ├── ARUtils.java
│ │ │ │ ├── DisplayUtils.java
│ │ │ │ ├── MD5.java
│ │ │ │ ├── NameUtils.java
│ │ │ │ ├── PermissionsCheckUtil.java
│ │ │ │ ├── ScreenUtils.java
│ │ │ │ └── ToastUtil.java
│ │ │ └── widgets/
│ │ │ ├── ARVideoView.java
│ │ │ ├── AppBaseDialogFragment.java
│ │ │ ├── BaseDialog.java
│ │ │ ├── CustomDialog.java
│ │ │ └── KeyboardDialogFragment.java
│ │ └── res/
│ │ ├── anim/
│ │ │ ├── push_bottom_in.xml
│ │ │ └── push_bottom_out.xml
│ │ ├── color/
│ │ │ └── select_text_host_input.xml
│ │ ├── drawable/
│ │ │ ├── bg_host_head.xml
│ │ │ ├── bg_list_item.xml
│ │ │ ├── bg_white.xml
│ │ │ ├── default_input_bg.xml
│ │ │ ├── line_anim.xml
│ │ │ ├── list_item_bg.xml
│ │ │ ├── selector_apply.xml
│ │ │ ├── shape_creat_btn_bg.xml
│ │ │ ├── shape_edittext_bg.xml
│ │ │ ├── shape_home_green_btn.xml
│ │ │ ├── shape_meet_id.xml
│ │ │ ├── shape_message.xml
│ │ │ ├── shape_popuwindow.xml
│ │ │ ├── shape_room_apply_audio_line.xml
│ │ │ ├── shape_room_apply_line.xml
│ │ │ ├── shape_room_hang_up_line.xml
│ │ │ ├── shape_room_member.xml
│ │ │ ├── shape_room_message.xml
│ │ │ └── shape_room_name.xml
│ │ ├── drawable-xxhdpi/
│ │ │ ├── selector_video_manager.xml
│ │ │ ├── shape_back_btn.xml
│ │ │ ├── shape_index_button.xml
│ │ │ └── shape_index_solid_button.xml
│ │ ├── layout/
│ │ │ ├── activity_audio_guest.xml
│ │ │ ├── activity_audio_hoster.xml
│ │ │ ├── activity_guest.xml
│ │ │ ├── activity_hoster.xml
│ │ │ ├── activity_live_list.xml
│ │ │ ├── dialog_base.xml
│ │ │ ├── dialogfragment_keyboard.xml
│ │ │ ├── empty_act_data.xml
│ │ │ ├── empty_no_line_data.xml
│ │ │ ├── fragment_line.xml
│ │ │ ├── item_audio_line.xml
│ │ │ ├── item_line.xml
│ │ │ ├── item_line_list.xml
│ │ │ ├── item_live.xml
│ │ │ ├── item_live_chat.xml
│ │ │ ├── item_member.xml
│ │ │ ├── item_more_futures.xml
│ │ │ └── layout_arvideo.xml
│ │ ├── values/
│ │ │ ├── attrs.xml
│ │ │ ├── colors.xml
│ │ │ ├── dimens.xml
│ │ │ ├── ids.xml
│ │ │ ├── strings.xml
│ │ │ └── styles.xml
│ │ └── values-w820dp/
│ │ └── dimens.xml
│ └── test/
│ └── java/
│ └── org/
│ └── ar/
│ └── rtmpc/
│ └── ExampleUnitTest.java
├── build.gradle
├── gradle/
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
*.iml
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures
#
*.iml.gradle/
local.properties/
.idea
.DS_Store
/build
/captures
### Android
template
# Built application files
*.apk
*.ap_
# Files for the ART/Dalvik VM
*.dex
# Java class files
*.class
# Generated files
bin/
gen/
out/
# Gradle files
.gradle/
build/
# Local configuration file (sdk path, etc)
local.properties
# Proguard folder generated by Eclipse
proguard/
# Log Files
*.log
# Android Studio Navigation editor temp files
.navigation/
# Android Studio captures folder
captures/
# Intellij
.idea/
workspace.xml
# Keystore files
*.jks
================================================
FILE: README.md
================================================
# 重要提醒
anyRTC 对该版本已经不再维护。[前往新版本](https://github.com/anyRTC/ArAndroidSDK).
**新版本功能如下:**
- 频道管理
- 音频管理
- 视频管理
- 音频文件播放及混音
- 音效文件播放管理
- CDN推流
- 本地推流CDN组件
- 本地播放器组件
- 跨频道流媒体转发
- 直播导入在线媒体流
- 视频双流模式
- 音频自采集自渲染
- 视频自采集自渲染
- 耳返功能
- 。。。
**公司网址: [www.anyrtc.io](https://www.anyrtc.io)**
### anyRTC-RTMPC-Android SDK for Android
### 简介
基于RTMP和RTC混合引擎的在线视频连麦互动直播
Android 直播(网络自适应码率RTMP publisher)、点播播放器(播放器经过专业优化,可实现秒开RTMP Player)、基于RTMP和RTC混合引擎的视频连麦互动(最多支持4人同时互动)
### 优势
- 商业级开源代码,高效稳定 超小内存占有率,移动直播针对性极致优化,代码冗余率极低
- iOS,Web,PC全平台适配,硬件编解码可保证99%的可用性
- 接口极简,推流:2个 拉流:2个
- 底层库C++核心库代码风格采用:Google code style
- 极简内核,无需再去深扒复杂的FFMpeg代码
- OpenH264软件编码,FFMpeg软件解码,FAAC/FAAD软件编解码,适配不同系统的硬件编解码统统包含
- 支持SRS、Nginx-RTMP等标准RTMP服务;同时支持各大CDN厂商的接入
### app体验
##### [点击下载](http://download.anyrtc.io/xuye)
### SDK集成
# > 方式一[  ](https://bintray.com/dyncanyrtc/ar_dev/rtmpc/_latestVersion)
添加Jcenter仓库 Gradle依赖:
```
dependencies {
compile 'org.ar:rtmpc_hybrid:3.1.1'
}
```
或者 Maven
```
org.ar
rtmpc_hybrid
3.1.1
pom
```
##### 编译环境
AndroidStudio
##### 运行环境
Android API 16+
真机运行
### 如何使用
##### 注册开发者信息
>如果您还未注册anyRTC开发者账号,请登录[anyRTC官网](http://www.anyrtc.io)注册及获取更多的帮助。
##### 替换开发者账号
在[anyRTC官网](http://www.anyrtc.io)获取了应用ID,应用Token后,替换DEMO中
**DeveloperInfo**类中的信息即可。推拉流地址需用自己的
### 操作步骤
1. 演示需要两部以及两部以上的手机,装上该demo.
2. 一部手机创建直播间,另外两部手机在主页,下拉刷新当前直播列表,点击列表进入直播间。
3. 游客端点击链接按钮,进行连麦。
### 完整文档
SDK集成,API介绍,详见官方完整文档:[点击查看](https://docs.anyrtc.io/v1/RTMPC/android.html)
### iOS 版 互动连麦
[AR-RTMPC-iOS](https://github.com/AnyRTC/anyRTC-RTMPC-iOS)
### 支持的系统平台
**Android** 4.0及以上
### 支持的CPU架构
**Android** arm64-v8a armeabi armeabi-v7a
### 注意事项
1. RTMPC SDK所有回调均在子线程中,所以在回调中操作UI等,应切换主线程。
2. 注意安卓6.0+动态权限处理。
3. 常见错误代码请参考[错误码查询](https://www.anyrtc.io/resoure)
### 技术支持
- anyRTC官方网址:[https://www.anyrtc.io](https://www.anyrtc.io/resoure)
- QQ技术咨询群:554714720
- 联系电话:021-65650071-816
- Email:hi@dync.cc
### 关于直播
本公司有一整套完整直播解决方案。本公司开发者平台www.anyrtc.io。除了基于RTMP协议的直播系统外,我公司还有基于WebRTC的时时交互直播系统、P2P呼叫系统、会议系统等。快捷集成SDK,便可让你的应用拥有时时通话功能。欢迎您的来电~
### License
- RTMPCEngine is available under the MIT license. See the LICENSE file for more info.
================================================
FILE: app/.gitignore
================================================
/build
================================================
FILE: app/build.gradle
================================================
apply plugin: 'com.android.application'
android {
compileSdkVersion 28
buildToolsVersion '27.0.3'
defaultConfig {
applicationId "org.ar.rtmpc"
minSdkVersion 16
targetSdkVersion 28
versionCode 1
versionName "3.0.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
repositories {
flatDir {
dirs 'libs'
}
}
}
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
compile 'com.android.support:appcompat-v7:28.0.0'
compile 'com.android.support:recyclerview-v7:28.0.0'
compile 'com.android.support:cardview-v7:28.0.0'
compile 'com.github.CymChad:BaseRecyclerViewAdapterHelper:2.9.22'
compile 'com.gyf.barlibrary:barlibrary:2.3.0'
compile 'com.yanzhenjie.permission:support:2.0.1'
compile 'com.jaredrummler:material-spinner:1.1.0'
compile 'com.android.support:design:28.0.0'
compile 'com.orhanobut:logger:1.15'
compile 'com.yanzhenjie.nohttp:okhttp:1.1.11'
compile 'org.ar:rtmpc_hybrid:3.1.0'
compile 'com.loopj.android:android-async-http:1.4.9'
}
================================================
FILE: app/gradle/wrapper/gradle-wrapper.properties
================================================
#Fri Mar 15 15:28:49 CST 2019
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
================================================
FILE: app/gradlew
================================================
#!/usr/bin/env sh
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"
================================================
FILE: app/gradlew.bat
================================================
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
================================================
FILE: app/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in D:\Android\android-sdk-windows/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# 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 *;
#}
-keepattributes InnerClasses
-dontoptimize
-optimizationpasses 7
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-dontpreverify
-verbose
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.app.backup.BackupAgentHelper
-keep public class * extends android.preference.Preference
-keep public class com.android.vending.licensing.ILicensingService
-keepclasseswithmembernames class * {
native ;
}
-keepclasseswithmembers class * {
public (android.content.Context, android.util.AttributeSet);
}
-keepclasseswithmembers class * {
public (android.content.Context, android.util.AttributeSet, int);
}
-keepclassmembers class * extends android.app.Activity {
public void *(android.view.View);
}
-keep public class * extends android.view.View {
public (android.content.Context);
public (android.content.Context, android.util.AttributeSet);
public (android.content.Context, android.util.AttributeSet, int);
public void set*(...);
}
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}
-keepnames class * implements java.io.Serializable
-keepclassmembers class * implements java.io.Serializable {
static final long serialVersionUID;
private static final java.io.ObjectStreamField[] serialPersistentFields;
!static !transient ;
private void writeObject(java.io.ObjectOutputStream);
private void readObject(java.io.ObjectInputStream);
java.lang.Object writeReplace();
java.lang.Object readResolve();
}
-keepattributes *Annotation*
-keepattributes Exceptions,InnerClasses,Signature
-keepattributes SourceFile,LineNumberTable
-keep class **.R$* { *; }
-dontwarn android.support.v4.**
-keep class android.support.v4.** { *; }
-keep interface android.support.v4.** { *; }
-keep public class * extends android.support.v4.**
-keep public class * extends android.app.Fragment
-keep class org.anyrtc.model.** { *; }
#anyrtc
-dontwarn org.anyrtc.rtmpc_hybrid.**
-keep class org.anyrtc.rtmpc_hybrid.**{*;}
-dontwarn org.webrtc.**
-keep class org.webrtc.**{*;}
-keep class com.gyf.barlibrary.* {*;}
#Andprermission
-keepclassmembers class ** {
@com.yanzhenjie.permission.PermissionYes ;
}
-keepclassmembers class ** {
@com.yanzhenjie.permission.PermissionNo ;
}
#BaseAdapter
-keep class com.chad.library.adapter.** {
*;
}
================================================
FILE: app/src/androidTest/java/org/ar/rtmpc/ApplicationTest.java
================================================
package org.ar.rtmpc;
import android.app.Application;
import android.test.ApplicationTestCase;
/**
* Testing Fundamentals
*/
public class ApplicationTest extends ApplicationTestCase {
public ApplicationTest() {
super(Application.class);
}
}
================================================
FILE: app/src/main/AndroidManifest.xml
================================================
================================================
FILE: app/src/main/java/org/ar/ARApplication.java
================================================
package org.ar;
import android.app.Application;
import com.yanzhenjie.nohttp.InitializationConfig;
import com.yanzhenjie.nohttp.NoHttp;
import org.ar.utils.NameUtils;
import org.ar.rtmpc_hybrid.ARRtmpcEngine;
/**
* Created by Skyline on 2016/8/3.
*/
public class ARApplication extends Application {
public static ARApplication mARApplication;
private static String NickName="";
public static String LIVE_ID=(int)((Math.random()*9+1)*100000)+"";//直播间ID
@Override
public void onCreate() {
super.onCreate();
mARApplication =this;
NickName= NameUtils.getNickName();
ARRtmpcEngine.Inst().initEngine(getApplicationContext(), DeveloperInfo.APPID, DeveloperInfo.APPTOKEN);
InitializationConfig config = InitializationConfig.newBuilder(this)
.connectionTimeout(15*1000)
.readTimeout(15*1000)
.retry(1).build();
NoHttp.initialize(config);
}
public static Application App(){
return mARApplication;
}
public static String getNickName(){
return NickName;
}
}
================================================
FILE: app/src/main/java/org/ar/BaseActivity.java
================================================
package org.ar;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import com.gyf.barlibrary.ImmersionBar;
/**
* Created by Skyline on 2016/5/24.
*/
public abstract class BaseActivity extends AppCompatActivity {
protected ImmersionBar mImmersionBar;
ProgressDialog pd;
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(this.getLayoutId());
mImmersionBar = ImmersionBar.with(this);
mImmersionBar.statusBarDarkFont(true,0.2f).init();
this.initView(savedInstanceState);
}
private void ProgressDialog(Context ctx) {
pd = new ProgressDialog(ctx);
pd.setProgressStyle(ProgressDialog.STYLE_SPINNER);
pd.setCancelable(true);
pd.setCanceledOnTouchOutside(false);
pd.show();
}
public void showProgressDialog() {
if (pd == null) {
ProgressDialog(this);
}
pd.setMessage("正在加载...");
pd.show();
}
public void hiddenProgressDialog() {
if (pd != null && pd.isShowing()) {
pd.cancel();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mImmersionBar != null)
mImmersionBar.destroy();
}
@Override
public void setContentView(int layoutResID) {
super.setContentView(layoutResID);
}
public void startAnimActivity(Class> cls) {
startActivity(new Intent(this, cls));
}
public void finishAnimActivity() {
finish();
}
public void startAnimActivity(Class> cls, Bundle bundle) {
Intent intent = new Intent(this, cls);
intent.putExtras(bundle);
startActivity(intent);
}
public abstract int getLayoutId();
public abstract void initView(Bundle savedInstanceState);
}
================================================
FILE: app/src/main/java/org/ar/DeveloperInfo.java
================================================
package org.ar;
public class DeveloperInfo {
public final static String APPID = "";
public final static String APPTOKEN = "";
public final static String PULL_URL = "";
public final static String PUSH_URL= "";
}
================================================
FILE: app/src/main/java/org/ar/SplashActivity.java
================================================
package org.ar;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import org.ar.guest.LiveListActivity;
public class SplashActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
startActivity(new Intent(this,LiveListActivity.class));
finish();
}
}
================================================
FILE: app/src/main/java/org/ar/adapter/AudioLineAdapter.java
================================================
package org.ar.adapter;
import android.view.View;
import android.widget.TextView;
import com.chad.library.adapter.base.BaseQuickAdapter;
import com.chad.library.adapter.base.BaseViewHolder;
import org.ar.rtmpc.R;
import org.ar.model.LineBean;
/**
* Created by liuxiaozhong on 2017-09-25.
*/
public class AudioLineAdapter extends BaseQuickAdapter {
boolean isHost;
public AudioLineAdapter(boolean isHost) {
super(R.layout.item_audio_line);
this.isHost=isHost;
}
@Override
protected void convert(BaseViewHolder helper, LineBean item) {
TextView hangup=helper.getView(R.id.tv_hangup);
helper.setText(R.id.tv_name,item.name);
helper.addOnClickListener(R.id.tv_hangup);
if (isHost){
hangup.setVisibility(View.VISIBLE);
}else {
if (item.isSelf){
hangup.setVisibility(View.VISIBLE);
}else {
hangup.setVisibility(View.INVISIBLE);
}
}
}
}
================================================
FILE: app/src/main/java/org/ar/adapter/LiveLineAdapter.java
================================================
package org.ar.adapter;
import android.widget.TextView;
import com.chad.library.adapter.base.BaseQuickAdapter;
import com.chad.library.adapter.base.BaseViewHolder;
import org.ar.rtmpc.R;
import org.ar.model.LineBean;
/**
* Created by liuxiaozhong on 2017/9/24.
*/
public class LiveLineAdapter extends BaseQuickAdapter {
public LiveLineAdapter() {
super(R.layout.item_line);
}
@Override
protected void convert(BaseViewHolder helper, LineBean item) {
TextView tv_name=helper.getView(R.id.tv_name);
tv_name.setText(item.name+"申请连麦");
helper.addOnClickListener(R.id.tv_agree);
helper.addOnClickListener(R.id.tv_refuse);
}
}
================================================
FILE: app/src/main/java/org/ar/adapter/LiveListAdapter.java
================================================
package org.ar.adapter;
import android.graphics.drawable.Drawable;
import android.widget.TextView;
import com.chad.library.adapter.base.BaseQuickAdapter;
import com.chad.library.adapter.base.BaseViewHolder;
import org.ar.rtmpc.R;
import org.ar.model.LiveBean;
/**
* Created by liuxiaozhong on 2017-09-14.
*/
public class LiveListAdapter extends BaseQuickAdapter {
public LiveListAdapter() {
super(R.layout.item_live);
}
@Override
protected void convert(BaseViewHolder helper, LiveBean item) {
helper.setText(R.id.tv_name,item.getmLiveTopic());
helper.setText(R.id.tv_num,item.getmMemberNum()+"");
TextView tvLiveType = helper.getView(R.id.tv_live_type);
Drawable imgVideo = helper.itemView.getContext().getResources().getDrawable(
R.drawable.img_video);
Drawable imgAdudio = helper.itemView.getContext().getResources().getDrawable(
R.drawable.img_audio);
if(item.isAudioLive==1) {
tvLiveType.setCompoundDrawablesWithIntrinsicBounds(imgAdudio,
null, null, null);
tvLiveType.setText("音频直播");
} else {
tvLiveType.setCompoundDrawablesWithIntrinsicBounds(imgVideo,
null, null, null);
tvLiveType.setText("视频");
}
}
}
================================================
FILE: app/src/main/java/org/ar/adapter/LiveMessageAdapter.java
================================================
package org.ar.adapter;
import android.graphics.Color;
import com.chad.library.adapter.base.BaseQuickAdapter;
import com.chad.library.adapter.base.BaseViewHolder;
import org.ar.model.MessageBean;
/**
* Created by liuxiaozhong on 2017-09-22.
*/
public class LiveMessageAdapter extends BaseQuickAdapter {
public LiveMessageAdapter() {
super(android.R.layout.simple_list_item_1);
}
@Override
protected void convert(BaseViewHolder helper, MessageBean item) {
helper.setTextColor(android.R.id.text1, item.type==1 ? Color.parseColor("#ffffff") : Color.parseColor("#666666"));
helper.setText(android.R.id.text1, item.name+":"+item.content);
}
}
================================================
FILE: app/src/main/java/org/ar/adapter/LogAdapter.java
================================================
package org.ar.adapter;
import android.graphics.Color;
import android.widget.TextView;
import com.chad.library.adapter.base.BaseQuickAdapter;
import com.chad.library.adapter.base.BaseViewHolder;
/**
* Created by liuxiaozhong on 2019/3/12.
*/
public class LogAdapter extends BaseQuickAdapter {
public LogAdapter() {
super(android.R.layout.simple_list_item_1);
}
@Override
protected void convert(BaseViewHolder helper, String item) {
TextView textView=helper.getView(android.R.id.text1);
textView.setTextColor(Color.parseColor("#666666"));
helper.setText(android.R.id.text1,item);
}
}
================================================
FILE: app/src/main/java/org/ar/guest/AudioGuestActivity.java
================================================
package org.ar.guest;
import android.content.DialogInterface;
import android.graphics.drawable.AnimationDrawable;
import android.media.AudioManager;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.support.v7.app.AlertDialog;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import com.chad.library.adapter.base.BaseQuickAdapter;
import org.ar.BaseActivity;
import org.ar.ARApplication;
import org.ar.adapter.AudioLineAdapter;
import org.ar.adapter.LiveMessageAdapter;
import org.ar.adapter.LogAdapter;
import org.ar.common.utils.ARAudioManager;
import org.ar.model.LineBean;
import org.ar.model.MessageBean;
import org.ar.utils.ARUtils;
import org.ar.utils.ToastUtil;
import org.ar.widgets.KeyboardDialogFragment;
import org.ar.common.enums.ARVideoCommon;
import org.ar.rtmpc_hybrid.ARRtmpcEngine;
import org.ar.rtmpc_hybrid.ARRtmpcGuestEvent;
import org.ar.rtmpc_hybrid.ARRtmpcGuestKit;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.Set;
/**
* 音频游客界面
*/
public class AudioGuestActivity extends BaseActivity implements BaseQuickAdapter.OnItemChildClickListener {
TextView tvTitle;
RecyclerView rvMsgList,rvLog;
TextView tvApplyLine;
View viewSpace;
TextView tvMemberNum;
RecyclerView rvLineList;
TextView tvRtmpOk;
TextView tvRtmpStatus;
TextView tvRtcOk;
TextView tvHostName;
ImageView ivLineAnim;
RelativeLayout rl_log_layout;
private ARRtmpcGuestKit mGuestKit;
private ARAudioManager mRtmpAudioManager = null;
private LiveMessageAdapter mAdapter;
private LogAdapter logAdapter;
private boolean isApplyLine = false;//是否在连麦、申请连麦
private boolean isLinling = false;
private AnimationDrawable hostAnimation;
private AudioLineAdapter audioLineAdapter;
private String liveId = "";
private String userID="guest"+(int)((Math.random()*9+1)*100000)+"";
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
if (isApplyLine) {
ShowExitDialog();
return false;
}
}
return super.onKeyDown(keyCode, event);
}
@Override
protected void onDestroy() {
super.onDestroy();
/**
* 销毁rtmp播放器
*/
if (mGuestKit != null) {
mGuestKit.clean();
mGuestKit = null;
}
if (mRtmpAudioManager != null) {
mRtmpAudioManager.stop();
mRtmpAudioManager = null;
}
}
@Override
public int getLayoutId() {
return org.ar.rtmpc.R.layout.activity_audio_guest;
}
@Override
public void initView(Bundle savedInstanceState) {
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); //保持屏幕常亮
viewSpace = findViewById(org.ar.rtmpc.R.id.view_space);
mImmersionBar.titleBar(viewSpace).init();
tvTitle = findViewById(org.ar.rtmpc.R.id.tv_title);
rl_log_layout=findViewById(org.ar.rtmpc.R.id.rl_log_layout);
rvLog=findViewById(org.ar.rtmpc.R.id.rv_log);
rvMsgList = findViewById(org.ar.rtmpc.R.id.rv_msg_list);
tvApplyLine = findViewById(org.ar.rtmpc.R.id.tv_apply_line);
tvMemberNum = findViewById(org.ar.rtmpc.R.id.tv_member_num);
rvLineList = findViewById(org.ar.rtmpc.R.id.rv_line_list);
tvRtmpOk = findViewById(org.ar.rtmpc.R.id.tv_rtmp_ok);
tvRtmpStatus = findViewById(org.ar.rtmpc.R.id.tv_rtmp_status);
tvRtcOk = findViewById(org.ar.rtmpc.R.id.tv_rtc_ok);
tvHostName = findViewById(org.ar.rtmpc.R.id.tv_host_name);
ivLineAnim = findViewById(org.ar.rtmpc.R.id.iv_line_anim);
hostAnimation = (AnimationDrawable) ivLineAnim.getBackground();
rvMsgList.setLayoutManager(new LinearLayoutManager(this));
mAdapter = new LiveMessageAdapter();
audioLineAdapter = new AudioLineAdapter(false);
audioLineAdapter.setOnItemChildClickListener(this);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
linearLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
rvLineList.setLayoutManager(linearLayoutManager);
rvLineList.setAdapter(audioLineAdapter);
rvLineList.setItemAnimator(null);
rvMsgList.setAdapter(mAdapter);
logAdapter = new LogAdapter();
rvLog.setLayoutManager(new LinearLayoutManager(this));
logAdapter.bindToRecyclerView(rvLog);
String pullUrl = getIntent().getStringExtra("pullURL");
String hostName=getIntent().getStringExtra("hostName");
tvHostName.setText(hostName);
liveId = getIntent().getStringExtra("liveId");
tvTitle.setText("房间ID:" + liveId);
mRtmpAudioManager = ARAudioManager.create(this);
mRtmpAudioManager.start(new ARAudioManager.AudioManagerEvents() {
@Override
public void onAudioDeviceChanged(ARAudioManager.AudioDevice audioDevice, Set set) {
}
});
ARRtmpcEngine.Inst().getGuestOption().setMediaType(ARVideoCommon.ARMediaType.Audio);
mGuestKit = new ARRtmpcGuestKit(mGuestListener);
mGuestKit.startRtmpPlay(pullUrl, 0);
mGuestKit.joinRTCLine("", liveId, userID, getUserData());
}
public String getUserData() {
JSONObject user = new JSONObject();
try {
user.put("isHost", 0);
user.put("userId", userID);
user.put("nickName", ARApplication.getNickName());
user.put("headUrl", "www.baidu.com");
} catch (JSONException e) {
e.printStackTrace();
}
return user.toString();
}
private void onAudioManagerChangedState() {
// TODO(henrika): disable video if
// AppRTCAudioManager.AudioDevice.EARPIECE
// is active.
setVolumeControlStream(AudioManager.STREAM_VOICE_CALL);
}
private void ShowExitDialog() {
AlertDialog.Builder build = new AlertDialog.Builder(this);
build.setTitle(org.ar.rtmpc.R.string.str_exit);
build.setMessage(org.ar.rtmpc.R.string.str_line_hangup);
build.setPositiveButton(org.ar.rtmpc.R.string.str_ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// TODO Auto-generated method stub
mGuestKit.hangupRTCLine();
isApplyLine = false;
isLinling = false;
finishAnimActivity();
}
});
build.setNegativeButton(org.ar.rtmpc.R.string.str_cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// TODO Auto-generated method stub
}
});
build.show();
}
/**
* 更新列表
*
* @param chatMessageBean
*/
private void addChatMessageList(MessageBean chatMessageBean) {
// 150 条 修改;
if (chatMessageBean == null) {
return;
}
if (mAdapter.getData().size() < 150) {
mAdapter.addData(chatMessageBean);
} else {
mAdapter.remove(0);
mAdapter.addData(chatMessageBean);
}
rvMsgList.smoothScrollToPosition(mAdapter.getData().size() - 1);
}
public void printLog(String log){
Log.d("RTMPC", log);
logAdapter.addData(log);
}
/**
* 观看直播回调信息接口
*/
private ARRtmpcGuestEvent mGuestListener = new ARRtmpcGuestEvent() {
/**
* rtmp 连接成功 视频即将播放;视频播放前的操作可以在此接口中进行操作
*/
@Override
public void onRtmpPlayerOk() {
AudioGuestActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
printLog("回调:onRtmpPlayerOk");
if (tvRtmpOk != null) {
tvRtmpOk.setText("Rtmp连接成功");
}
}
});
}
/**
* rtmp 开始播放 视频开始播放
*/
@Override
public void onRtmpPlayerStart() {
AudioGuestActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
printLog("回调:onRtmpPlayerStart");
}
});
}
/**
* rtmp 当前播放状态
* @param cacheTime 当前缓存时间
* @param curBitrate 当前播放器码流
*/
@Override
public void onRtmpPlayerStatus(final int cacheTime, final int curBitrate) {
AudioGuestActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
printLog("回调:onRtmpPlayerStatus cacheTime:" + cacheTime + " curBitrate:" + curBitrate);
if (tvRtmpStatus != null) {
tvRtmpStatus.setText("当前缓存时间:" + cacheTime + " ms" + "\n当前码流:" + curBitrate / 10024 / 8 + "kb/s");
}
}
});
}
/**
* rtmp 播放缓冲区时长
* @param nPercent 缓冲时间
*/
@Override
public void onRtmpPlayerLoading(final int nPercent) {
AudioGuestActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
printLog("回调:onRtmpPlayerCache nPercent:" + nPercent);
}
});
}
/**
* rtmp 播放器关闭
* @param nCode
*/
@Override
public void onRtmpPlayerClosed(final int nCode) {
AudioGuestActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
printLog("回调:onRtmpPlayerClosed nCode:" + nCode);
}
});
}
/**
* 游客RTC 状态回调
* @param nCode 回调响应码:0:正常;101:主播未开启直播;
*/
@Override
public void onRTCJoinLineResult(final int nCode, String s) {
AudioGuestActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
printLog("回调:onRTCJoinLineResult nCode:" + nCode);
if (nCode == 0) {
if (tvRtcOk != null) {
tvRtcOk.setText(org.ar.rtmpc.R.string.str_rtc_connect_success);
}
} else if (nCode == 101) {
Toast.makeText(AudioGuestActivity.this, org.ar.rtmpc.R.string.str_hoster_not_live, Toast.LENGTH_LONG).show();
if (tvRtcOk != null) {
tvRtcOk.setText(org.ar.rtmpc.R.string.str_rtc_connect_success);
}
} else {
if (tvRtcOk != null) {
tvRtcOk.setText(ARUtils.getErrString(nCode));
}
}
}
});
}
/**
* 游客申请连线结果
* @param nCode 0:申请连线成功;-1:主播拒绝连线
*/
@Override
public void onRTCApplyLineResult(final int nCode) {
AudioGuestActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
printLog("回调:onRTCApplyLineResult nCode:" + nCode);
if (nCode == 0) {
isApplyLine = true;
tvApplyLine.setText("挂断");
audioLineAdapter.addData(0, new LineBean("self", "自己", true));
isLinling = true;
tvApplyLine.setBackgroundResource(org.ar.rtmpc.R.drawable.shape_room_hang_up_line);
} else if (nCode == 601) {
Toast.makeText(AudioGuestActivity.this, org.ar.rtmpc.R.string.str_hoster_refused, Toast.LENGTH_LONG).show();
isApplyLine = false;
isLinling = false;
tvApplyLine.setText("连麦");
tvApplyLine.setBackgroundResource(org.ar.rtmpc.R.drawable.shape_room_apply_line);
}
}
});
}
/**
* 挂断连线回调
*/
@Override
public void onRTCHangupLine() {
//主播连线断开
AudioGuestActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
printLog("回调:onRTCHangupLine ");
mGuestKit.hangupRTCLine();
audioLineAdapter.remove(0);
tvApplyLine.setText(org.ar.rtmpc.R.string.str_connect_hoster);
tvApplyLine.setBackgroundResource(org.ar.rtmpc.R.drawable.shape_room_apply_line);
isApplyLine = false;
isLinling = false;
}
});
}
@Override
public void onRTCOpenRemoteVideoRender(String s, String s1, String s2, String s3) {
}
@Override
public void onRTCCloseRemoteVideoRender(String s, String s1, String s2) {
}
@Override
public void onRTCOpenRemoteAudioLine(final String strLivePeerId, final String strUserId, final String strUserData) {
AudioGuestActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
printLog("回调:onRTCOpenAudioLine strLivePeerId:" + strLivePeerId + "strUserId:" + strUserId + " strUserData:" + strUserData);
try {
JSONObject jsonObject = new JSONObject(strUserData);
if (strLivePeerId.equals("RTMPC_Line_Hoster")) {
// audioLineAdapter.addData(new LineBean(strLivePeerId, jsonObject.getString("nickName"), true));
} else {
audioLineAdapter.addData(new LineBean(strLivePeerId, jsonObject.getString("nickName"), false));
}
} catch (JSONException e) {
e.printStackTrace();
}
}
});
}
@Override
public void onRTCCloseRemoteAudioLine(final String strLivePeerId, final String strUserId) {
AudioGuestActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
printLog("回调:onRTCCloseAudioLine strLivePeerId:" + strLivePeerId + "strUserId:" + strUserId);
int index = 9;
for (int i = 0; i < audioLineAdapter.getData().size(); i++) {
if (audioLineAdapter.getItem(i).peerId.equals(strLivePeerId)) {
index = i;
}
}
if (index != 9 && index <= audioLineAdapter.getData().size()) {
audioLineAdapter.remove(index);
}
}
});
}
@Override
public void onRTCLocalAudioActive(int i) {
AudioGuestActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
Log.d("RTMPC", "onRTCLocalAudioActive");
}
});
}
@Override
public void onRTCHosterAudioActive(int i) {
AudioGuestActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
Log.d("RTMPC", "onRTCHosterAudioActive");
}
});
}
@Override
public void onRTCRemoteAudioActive(final String strLivePeerId, final String strUserId, final int nTime) {
AudioGuestActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
printLog("回调:onRTCAudioActive strLivePeerId:" + strLivePeerId + "strUserId:" + strUserId + " nTime:" + nTime);
if (strLivePeerId.equals("RTMPC_Hoster")) {//主播
ivLineAnim.setVisibility(View.VISIBLE);
hostAnimation.start();
} else {
for (int i = 0; i < audioLineAdapter.getData().size(); i++) {
if (strLivePeerId.equals(audioLineAdapter.getData().get(i).peerId)) {
audioLineAdapter.getItem(i).setStartAnim(true);
audioLineAdapter.notifyItemChanged(i);
}
}
}
}
});
}
@Override
public void onRTCRemoteAVStatus(final String s, boolean b, boolean b1) {
AudioGuestActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
printLog("回调:onRTCRemoteAVStatus peerID:" + s);
}
});
}
/**
* 主播已离开回调
* @param nCode
*/
@Override
public void onRTCLineLeave(final int nCode, String s) {
//主播关闭直播
AudioGuestActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
printLog("回调:onRTCLineLeave nCode:" + nCode);
if (mGuestKit != null) {
mGuestKit.stopRtmpPlay();
}
finishAnimActivity();
ToastUtil.show("主播已离开");
}
});
}
/**
* 消息回调
* @param strCustomID 消息的发送者id
* @param strCustomName 消息的发送者昵称
* @param strCustomHeader 消息的发送者头像url
* @param strMessage 消息内容
*/
@Override
public void onRTCUserMessage(final int nType, final String strCustomID, final String strCustomName, final String strCustomHeader, final String strMessage) {
AudioGuestActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
printLog("回调:onRTCUserMessage nType:" + nType + "strCustomID:" + strCustomID + "strCustomName:" + strCustomName + "strCustomHeader:" + strCustomHeader + "strMessage:" + strMessage);
addChatMessageList(new MessageBean(MessageBean.AUDIO, strCustomName, strMessage));
}
});
}
/**
* 观看直播的总人数回调
* @param totalMembers 观看直播的总人数
*/
@Override
public void onRTCMemberNotify(final String strServerId, final String strRoomId, final int totalMembers) {
AudioGuestActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
printLog("回调:onRTCMemberNotify strServerId:" + strServerId + "strRoomId:" + strRoomId + "totalMembers:" + totalMembers);
tvMemberNum.setText("在线观看人数" + totalMembers + "");
}
});
}
};
public void onClick(View view) {
switch (view.getId()) {
case org.ar.rtmpc.R.id.btn_close:
if (isLinling) {
ShowExitDialog();
} else {
finishAnimActivity();
}
break;
case org.ar.rtmpc.R.id.iv_message:
showChatLayout();
break;
case org.ar.rtmpc.R.id.tv_apply_line:
if (isApplyLine) {
if (mGuestKit != null) {
mGuestKit.hangupRTCLine();
if (isLinling){
audioLineAdapter.remove(0);
}
tvApplyLine.setText("连麦");
tvApplyLine.setBackgroundResource(org.ar.rtmpc.R.drawable.shape_room_apply_line);
isApplyLine = false;
isLinling = false;
}
} else {
if (mGuestKit != null) {
mGuestKit.applyRTCLine();
tvApplyLine.setText("挂断");
tvApplyLine.setBackgroundResource(org.ar.rtmpc.R.drawable.shape_room_hang_up_line);
isApplyLine = true;
isLinling = false;
}
}
break;
case org.ar.rtmpc.R.id.btn_log:
rl_log_layout.setVisibility(View.VISIBLE);
break;
case org.ar.rtmpc.R.id.ibtn_close_log:
rl_log_layout.setVisibility(View.GONE);
break;
}
}
private void showChatLayout() {
KeyboardDialogFragment keyboardDialogFragment = new KeyboardDialogFragment();
keyboardDialogFragment.show(getSupportFragmentManager(), "KeyboardDialogFragment");
keyboardDialogFragment.setEdittextListener(new KeyboardDialogFragment.EdittextListener() {
@Override
public void setTextStr(String text) {
addChatMessageList(new MessageBean(MessageBean.AUDIO, ARApplication.getNickName(), text));
mGuestKit.sendMessage(0, ARApplication.getNickName(), "", text);
}
@Override
public void dismiss(DialogFragment dialogFragment) {
}
});
}
@Override
public void onItemChildClick(BaseQuickAdapter adapter, View view, int position) {
switch (view.getId()) {
case org.ar.rtmpc.R.id.tv_hangup:
if (mGuestKit != null) {
mGuestKit.hangupRTCLine();
tvApplyLine.setBackgroundResource(org.ar.rtmpc.R.drawable.shape_room_apply_line);
tvApplyLine.setText("连麦");
audioLineAdapter.remove(0);
isApplyLine = false;
isLinling = false;
}
break;
}
}
}
================================================
FILE: app/src/main/java/org/ar/guest/GuestActivity.java
================================================
package org.ar.guest;
import android.annotation.SuppressLint;
import android.content.DialogInterface;
import android.media.AudioManager;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.support.v7.app.AlertDialog;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import org.ar.BaseActivity;
import org.ar.ARApplication;
import org.ar.adapter.LiveMessageAdapter;
import org.ar.adapter.LogAdapter;
import org.ar.common.utils.ARAudioManager;
import org.ar.model.MessageBean;
import org.ar.rtmpc.R;
import org.ar.utils.ARUtils;
import org.ar.utils.ToastUtil;
import org.ar.widgets.ARVideoView;
import org.ar.widgets.KeyboardDialogFragment;
import org.ar.common.enums.ARVideoCommon;
import org.ar.rtmpc_hybrid.ARRtmpcEngine;
import org.ar.rtmpc_hybrid.ARRtmpcGuestEvent;
import org.ar.rtmpc_hybrid.ARRtmpcGuestKit;
import org.json.JSONException;
import org.json.JSONObject;
import org.webrtc.VideoRenderer;
import java.util.Set;
/**
* 视频游客页面
*/
public class GuestActivity extends BaseActivity {
RelativeLayout rlRtmpcVideos,rl_log_layout;
TextView tvTitle;
TextView tvRtmpOk;
TextView tvRtmpStatus;
TextView tvRtcOk;
RecyclerView rvMsgList,rvLog;
TextView tvApplyLine;
View viewSpace;
TextView tvMemberNum;
ImageButton ibtnCamera;
private ARRtmpcGuestKit mGuestKit;
private ARVideoView mVideoView;
private ARAudioManager mRtmpAudioManager = null;
private LiveMessageAdapter mAdapter;
private LogAdapter logAdapter;
private boolean isApplyLine = false;//是否申请连麦
private boolean isLining = false;//是否正在连麦
private String liveId = "";
private String userId="guest"+(int)((Math.random()*9+1)*100000)+"";
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
if (isApplyLine) {
ShowExitDialog();
return false;
}
}
return super.onKeyDown(keyCode, event);
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mRtmpAudioManager != null) {
mRtmpAudioManager.stop();
mRtmpAudioManager = null;
}
/**
* 销毁rtmp播放器
*/
if (mGuestKit != null) {
mGuestKit.clean();
mVideoView.removeLocalVideoRender();
mGuestKit = null;
}
}
@Override
public int getLayoutId() {
return org.ar.rtmpc.R.layout.activity_guest;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); //保持屏幕常亮
super.onCreate(savedInstanceState);
}
@Override
public void initView(Bundle savedInstanceState) {
viewSpace=findViewById(org.ar.rtmpc.R.id.view_space);
mImmersionBar.titleBar(viewSpace).init();
ibtnCamera=findViewById(org.ar.rtmpc.R.id.btn_camare);
rlRtmpcVideos=findViewById(org.ar.rtmpc.R.id.rl_rtmpc_videos);
rl_log_layout=findViewById(org.ar.rtmpc.R.id.rl_log_layout);
rvLog=findViewById(org.ar.rtmpc.R.id.rv_log);
tvTitle=findViewById(org.ar.rtmpc.R.id.tv_title);
tvRtmpOk=findViewById(org.ar.rtmpc.R.id.tv_rtmp_ok);
tvRtmpStatus=findViewById(org.ar.rtmpc.R.id.tv_rtmp_status);
tvRtcOk=findViewById(org.ar.rtmpc.R.id.tv_rtc_ok);
rvMsgList=findViewById(org.ar.rtmpc.R.id.rv_msg_list);
tvApplyLine=findViewById(org.ar.rtmpc.R.id.tv_apply_line);
tvMemberNum=findViewById(org.ar.rtmpc.R.id.tv_member_num);
logAdapter = new LogAdapter();
rvLog.setLayoutManager(new LinearLayoutManager(this));
logAdapter.bindToRecyclerView(rvLog);
rvMsgList.setLayoutManager(new LinearLayoutManager(this));
mAdapter = new LiveMessageAdapter();
rvMsgList.setAdapter(mAdapter);
ARRtmpcEngine.Inst().getGuestOption().setDefaultFrontCamera(true);
ARRtmpcEngine.Inst().getGuestOption().setMediaType(ARVideoCommon.ARMediaType.Video);
String pullUrl = getIntent().getStringExtra("pullURL");
liveId = getIntent().getStringExtra("liveId");
tvTitle.setText("房间ID:" +liveId);
mVideoView = new ARVideoView( rlRtmpcVideos, ARRtmpcEngine.Inst().egl(), this,false, false);
mVideoView.setVideoViewLayout(false, Gravity.RIGHT, LinearLayout.VERTICAL);
mVideoView.setVideoLayoutOnclickEvent(new ARVideoView.VideoLayoutOnclickEvent() {
@Override
public void onCloseVideoRender(View view, String strPeerId) {
/**
* 挂断连线
*/
mGuestKit.hangupRTCLine();
mVideoView.removeRemoteRender("LocalCameraRender");
tvApplyLine.setText(org.ar.rtmpc.R.string.str_connect_hoster);
tvApplyLine.setBackgroundResource(org.ar.rtmpc.R.drawable.shape_room_apply_line);
isApplyLine = false;
isLining=false;
}
});
mRtmpAudioManager = ARAudioManager.create(this);
mRtmpAudioManager.start(new ARAudioManager.AudioManagerEvents() {
@Override
public void onAudioDeviceChanged(ARAudioManager.AudioDevice audioDevice, Set set) {
}
});
ARRtmpcEngine.Inst().getGuestOption().setMediaType(ARVideoCommon.ARMediaType.Video);
mGuestKit = new ARRtmpcGuestKit(mGuestListener);
mGuestKit.setAudioActiveCheck(true);
VideoRenderer render = mVideoView.openLocalVideoRender();
mGuestKit.startRtmpPlay(pullUrl, render.GetRenderPointer());
mGuestKit.joinRTCLine("", liveId, userId, getUserData());
}
public String getUserData() {
JSONObject user = new JSONObject();
try {
user.put("isHost", 0);
user.put("userId", userId);
user.put("nickName", ARApplication.getNickName());
user.put("headUrl", "www.baidu.com");
} catch (JSONException e) {
e.printStackTrace();
}
return user.toString();
}
private void onAudioManagerChangedState() {
// TODO(henrika): disable video if
// AppRTCAudioManager.AudioDevice.EARPIECE
// is active.
setVolumeControlStream(AudioManager.STREAM_VOICE_CALL);
}
private void ShowExitDialog() {
AlertDialog.Builder build = new AlertDialog.Builder(this);
build.setTitle(org.ar.rtmpc.R.string.str_exit);
build.setMessage(org.ar.rtmpc.R.string.str_line_hangup);
build.setPositiveButton(org.ar.rtmpc.R.string.str_ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// TODO Auto-generated method stub
mGuestKit.hangupRTCLine();
mVideoView.removeRemoteRender("LocalCameraRender");
finishAnimActivity();
}
});
build.setNegativeButton(org.ar.rtmpc.R.string.str_cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// TODO Auto-generated method stub
}
});
build.show();
}
/**
* 更新列表
*
* @param chatMessageBean
*/
private void addChatMessageList(MessageBean chatMessageBean) {
// 150 条 修改;
if (chatMessageBean == null) {
return;
}
if (mAdapter.getData().size() < 150) {
mAdapter.addData(chatMessageBean);
} else {
mAdapter.remove(0);
mAdapter.addData(chatMessageBean);
}
rvMsgList.smoothScrollToPosition(mAdapter.getData().size() - 1);
}
public void printLog(String log){
Log.d("RTMPC", log);
logAdapter.addData(log);
}
/**
* 观看直播回调信息接口
*/
private ARRtmpcGuestEvent mGuestListener = new ARRtmpcGuestEvent() {
/**
* rtmp 连接成功 视频即将播放;视频播放前的操作可以在此接口中进行操作
*/
@Override
public void onRtmpPlayerOk() {
GuestActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
printLog("回调:onRtmpPlayerOk");
if (tvRtmpOk != null) {
tvRtmpOk.setText("Rtmp连接成功");
}
}
});
}
/**
* rtmp 开始播放 视频开始播放
*/
@Override
public void onRtmpPlayerStart() {
GuestActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
printLog("回调:onRtmpPlayerStart");
}
});
}
/**
* rtmp 当前播放状态
* @param cacheTime 当前缓存时间
* @param curBitrate 当前播放器码流
*/
@Override
public void onRtmpPlayerStatus(final int cacheTime, final int curBitrate) {
GuestActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
printLog("回调:onRtmpPlayerStatus cacheTime:" + cacheTime + " curBitrate:" + curBitrate);
if (tvRtmpStatus != null) {
tvRtmpStatus.setText("当前缓存时间:" + cacheTime + " ms" + "\n当前码流:" + curBitrate / 10024 / 8 + "kb/s");
}
}
});
}
/**
* rtmp 播放缓冲区时长
* @param nPercent 缓冲时间
*/
@Override
public void onRtmpPlayerLoading(final int nPercent) {
GuestActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
printLog("回调:onRtmpPlayerCache nPercent:" + nPercent);
}
});
}
/**
* rtmp 播放器关闭
* @param nCode
*/
@Override
public void onRtmpPlayerClosed(final int nCode) {
GuestActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
printLog("回调:onRtmpPlayerClosed nCode:" + nCode);
}
});
}
/**
* 游客RTC 状态回调
* @param nCode 回调响应码:0:正常;101:主播未开启直播;
*/
@Override
public void onRTCJoinLineResult(final int nCode, String s) {
GuestActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
printLog("回调:onRTCJoinLineResult nCode:" + nCode);
if (tvRtcOk != null) {
if (nCode == 0) {
tvRtcOk.setText(org.ar.rtmpc.R.string.str_rtc_connect_success);
} else if (nCode == 101) {
Toast.makeText(GuestActivity.this, org.ar.rtmpc.R.string.str_hoster_not_live, Toast.LENGTH_LONG).show();
tvRtcOk.setText(org.ar.rtmpc.R.string.str_rtc_connect_success);
} else {
tvRtcOk.setText(ARUtils.getErrString(nCode));
}
}
}
});
}
/**
* 游客申请连线回调
*/
@Override
public void onRTCApplyLineResult(final int nCode) {
GuestActivity.this.runOnUiThread(new Runnable() {
@SuppressLint("MissingPermission")
@Override
public void run() {
printLog("回调:onRTCApplyLineResult nCode:" + nCode);
if (nCode == 0) {
ibtnCamera.setVisibility(View.VISIBLE);
isApplyLine = true;
isLining = true;
tvApplyLine.setText("挂断");
tvApplyLine.setBackgroundResource(org.ar.rtmpc.R.drawable.shape_room_hang_up_line);
VideoRenderer render = mVideoView.openRemoteVideoRender("LocalCameraRender");
mGuestKit.setLocalVideoCapturer(render.GetRenderPointer());
} else if (nCode == 601) {
Toast.makeText(GuestActivity.this, org.ar.rtmpc.R.string.str_hoster_refused, Toast.LENGTH_LONG).show();
isApplyLine = false;
ibtnCamera.setVisibility(View.GONE);
tvApplyLine.setText("连麦");
tvApplyLine.setBackgroundResource(org.ar.rtmpc.R.drawable.shape_room_apply_line);
}
}
});
}
/**
* 挂断连线回调
*/
@Override
public void onRTCHangupLine() {
//主播连线断开
GuestActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
printLog("回调:onRTCHangupLine ");
mGuestKit.hangupRTCLine();
ibtnCamera.setVisibility(View.GONE);
mVideoView.removeRemoteRender("LocalCameraRender");
tvApplyLine.setText(org.ar.rtmpc.R.string.str_connect_hoster);
tvApplyLine.setBackgroundResource(org.ar.rtmpc.R.drawable.shape_room_apply_line);
isApplyLine = false;
isLining = false;
}
});
}
@Override
public void onRTCOpenRemoteVideoRender(final String strLivePeerId, final String strPublishId, final String strUserId, final String strUserData) {
GuestActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
printLog("回调:onRTCOpenVideoRenderLeave strLivePeerId:" + strLivePeerId + "strUserId:" + strUserId + " strUserData:" + strUserData);
final VideoRenderer render = mVideoView.openRemoteVideoRender(strLivePeerId);
mGuestKit.setRTCRemoteVideoRender(strPublishId, render.GetRenderPointer());
}
});
}
@Override
public void onRTCCloseRemoteVideoRender(final String strLivePeerId, final String strPublishId, final String strUserId) {
GuestActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
Log.d("RTMPC", "onRTCCloseVideoRender strLivePeerId:" + strLivePeerId + "strUserId:" + strUserId);
if (mGuestKit != null && mVideoView != null ) {
mGuestKit.setRTCRemoteVideoRender(strPublishId, 0);
mVideoView.removeRemoteRender(strLivePeerId);
}
}
});
}
@Override
public void onRTCOpenRemoteAudioLine(String s, String s1, String s2) {
}
@Override
public void onRTCCloseRemoteAudioLine(String s, String s1) {
}
@Override
public void onRTCLocalAudioActive(int i) {
GuestActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
Log.d("RTMPC", "onRTCLocalAudioActive ");
}
});
}
@Override
public void onRTCHosterAudioActive(int i) {
GuestActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
Log.d("RTMPC", "onRTCHosterAudioActive ");
}
});
}
@Override
public void onRTCRemoteAudioActive(final String s, String s1, int i) {
GuestActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
printLog("回调:onRTCRemoteAudioActive peerID:" + s);
}
});
}
@Override
public void onRTCRemoteAVStatus(final String s, boolean b, boolean b1) {
GuestActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
printLog("回调:onRTCRemoteAVStatus peerID:" + s);
}
});
}
/**
* 主播已离开回调
*/
@Override
public void onRTCLineLeave(final int nCode, String s) {
//主播关闭直播
GuestActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
printLog("回调:onRTCLineLeave nCode:" + nCode);
if (mGuestKit != null) {
mGuestKit.stopRtmpPlay();
}
if (nCode == 0) {
ToastUtil.show("主播已离开");
} else if (nCode == 100) {
ToastUtil.show("网络已断开");
}
finishAnimActivity();
}
});
}
/**
* 连线接通后回调
* @param strLivePeerId
*/
/**
* 连线关闭后图像回调
* @param strLivePeerId
*/
/**
* 消息回调
* @param strCustomID 消息的发送者id
* @param strCustomName 消息的发送者昵称
* @param strCustomHeader 消息的发送者头像url
* @param strMessage 消息内容
*/
@Override
public void onRTCUserMessage(final int nType, final String strCustomID, final String strCustomName, final String strCustomHeader, final String strMessage) {
GuestActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
printLog("回调:onRTCUserMessage nType:" + nType + "strCustomID:" + strCustomID + "strCustomName:" + strCustomName + "strCustomHeader:" + strCustomHeader + "strMessage:" + strMessage);
addChatMessageList(new MessageBean(MessageBean.VIDEO, strCustomName, strMessage));
}
});
}
/**
* 观看直播的总人数回调
* @param totalMembers 观看直播的总人数
*/
@Override
public void onRTCMemberNotify(final String strServerId, final String strRoomId, final int totalMembers) {
GuestActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
printLog("回调:onRTCMemberNotify strServerId:" + strServerId + "strRoomId:" + strRoomId + "totalMembers:" + totalMembers);
if (tvMemberNum != null) {
tvMemberNum.setText("在线人数" + totalMembers + "");
}
}
});
}
};
public void onClick(View view) {
switch (view.getId()) {
case org.ar.rtmpc.R.id.btn_close:
if (isLining) {
ShowExitDialog();
} else {
finishAnimActivity();
}
break;
case org.ar.rtmpc.R.id.iv_message:
showChatLayout();
break;
case org.ar.rtmpc.R.id.tv_apply_line:
if (isApplyLine) {
if (mGuestKit != null) {
mGuestKit.hangupRTCLine();
tvApplyLine.setText("连麦");
ibtnCamera.setVisibility(View.GONE);
tvApplyLine.setBackgroundResource(org.ar.rtmpc.R.drawable.shape_room_apply_line);
mVideoView.removeRemoteRender("LocalCameraRender");
isApplyLine = false;
}
} else {
if (mGuestKit != null) {
mGuestKit.applyRTCLine();
tvApplyLine.setText("挂断");
tvApplyLine.setBackgroundResource(org.ar.rtmpc.R.drawable.shape_room_hang_up_line);
isApplyLine = true;
}
}
break;
case org.ar.rtmpc.R.id.btn_log:
rl_log_layout.setVisibility(View.VISIBLE);
break;
case org.ar.rtmpc.R.id.ibtn_close_log:
rl_log_layout.setVisibility(View.GONE);
break;
case R.id.btn_camare:
mGuestKit.switchCamera();
break;
}
}
private void showChatLayout() {
KeyboardDialogFragment keyboardDialogFragment = new KeyboardDialogFragment();
keyboardDialogFragment.show(getSupportFragmentManager(), "KeyboardDialogFragment");
keyboardDialogFragment.setEdittextListener(new KeyboardDialogFragment.EdittextListener() {
@Override
public void setTextStr(String text) {
addChatMessageList(new MessageBean(MessageBean.VIDEO, ARApplication.getNickName(), text));
mGuestKit.sendMessage(0, ARApplication.getNickName(), "", text);
}
@Override
public void dismiss(DialogFragment dialogFragment) {
}
});
}
}
================================================
FILE: app/src/main/java/org/ar/guest/LiveListActivity.java
================================================
package org.ar.guest;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import com.chad.library.adapter.base.BaseQuickAdapter;
import com.yanzhenjie.nohttp.NoHttp;
import com.yanzhenjie.nohttp.RequestMethod;
import com.yanzhenjie.nohttp.rest.Response;
import com.yanzhenjie.nohttp.rest.SimpleResponseListener;
import com.yanzhenjie.nohttp.rest.StringRequest;
import com.yanzhenjie.permission.AndPermission;
import com.yanzhenjie.permission.runtime.Permission;
import org.ar.BaseActivity;
import org.ar.ARApplication;
import org.ar.adapter.LiveListAdapter;
import org.ar.hoster.AudioHosterActivity;
import org.ar.hoster.HosterActivity;
import org.ar.rtmpc.BuildConfig;
import org.ar.model.LiveBean;
import org.ar.DeveloperInfo;
import org.ar.utils.MD5;
import org.ar.utils.PermissionsCheckUtil;
import org.ar.utils.ToastUtil;
import org.ar.rtmpc_hybrid.ARRtmpcHttpKit;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.List;
public class LiveListActivity extends BaseActivity implements SwipeRefreshLayout.OnRefreshListener, BaseQuickAdapter.OnItemClickListener, View.OnClickListener {
RecyclerView rvList;
SwipeRefreshLayout swipeRefresh;
LiveListAdapter mAdapter;
private List live_list = new ArrayList<>();
Button btn_video, btn_audio;
TextView tvVersion;
@Override
public int getLayoutId() {
return org.ar.rtmpc.R.layout.activity_live_list;
}
@Override
public void initView(Bundle savedInstanceState) {
tvVersion=findViewById(org.ar.rtmpc.R.id.tv_version);
tvVersion.setText("v "+ BuildConfig.VERSION_NAME);
btn_video = findViewById(org.ar.rtmpc.R.id.btn_video);
btn_audio = findViewById(org.ar.rtmpc.R.id.btn_audio);
btn_video.setOnClickListener(this);
btn_audio.setOnClickListener(this);
rvList = findViewById(org.ar.rtmpc.R.id.rv_list);
swipeRefresh = findViewById(org.ar.rtmpc.R.id.swipe_refresh);
mAdapter = new LiveListAdapter();
mAdapter.setOnItemClickListener(this);
mAdapter.setEmptyView(getEmptyView());
swipeRefresh.setOnRefreshListener(this);
rvList.setLayoutManager(new LinearLayoutManager(this,LinearLayoutManager.HORIZONTAL,false));
rvList.setAdapter(mAdapter);
AndPermission.with(this).runtime().permission(Permission.RECORD_AUDIO,Permission.CAMERA).start();
}
@Override
protected void onResume() {
super.onResume();
getLiveList();
}
public View getEmptyView(){
View view=View.inflate(this, org.ar.rtmpc.R.layout.empty_act_data,null);
TextView tvReGet= (TextView) view.findViewById(org.ar.rtmpc.R.id.tv_reget);
tvReGet.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
getLiveList();
}
});
return view;
}
@Override
public void onRefresh() {
getLiveList();
}
@Override
public void onItemClick(BaseQuickAdapter adapter, View view, final int position) {
if (AndPermission.hasPermissions(LiveListActivity.this,Permission.CAMERA,Permission.RECORD_AUDIO)){
Intent intent = new Intent(LiveListActivity.this, mAdapter.getItem(position).getIsAudioLive()==1 ? AudioGuestActivity.class : GuestActivity.class);
intent.putExtra("pullURL", mAdapter.getItem(position).getmRtmpPullUrl());
intent.putExtra("liveId",mAdapter.getItem(position).getmAnyrtcId());
intent.putExtra("hostName",mAdapter.getItem(position).getmHostName());
startActivity(intent);
}else {
PermissionsCheckUtil.showMissingPermissionDialog(LiveListActivity.this, "请先开启录音和相机权限");
}
}
private void getLiveList() {
ARRtmpcHttpKit.getAuthLivingList(this, new ARRtmpcHttpKit.RTMPCHttpCallback() {
@Override
public void OnRTMPCHttpOK(String s) {
if (swipeRefresh != null) {
swipeRefresh.setRefreshing(false);
}
if (TextUtils.isEmpty(s)) {
return;
}
try {
live_list.clear();
JSONObject jsonObject = new JSONObject(s);
if (jsonObject.has("LiveList")) {
JSONArray liveList = jsonObject.getJSONArray("LiveList");
JSONArray member = jsonObject.getJSONArray("LiveMembers");
for (int i = 0; i < liveList.length(); i++) {
JSONObject itemJson = new JSONObject(liveList.getString(i));
LiveBean bean = new LiveBean();
bean.setmRtmpPullUrl(itemJson.getString("rtmpUrl"));
bean.setmHlsUrl(itemJson.getString("hlsUrl"));
bean.setmLiveTopic(itemJson.getString("liveTopic"));
bean.setIsAudioLive(itemJson.getInt("isAudioLive"));
bean.setmAnyrtcId(itemJson.getString("anyrtcId"));
bean.setmHostName(itemJson.getString("hosterName"));
if (i <= member.length()) {
bean.setmMemberNum(member.get(i).toString());
}
live_list.add(bean);
}
mAdapter.setNewData(live_list);
}
} catch (JSONException e) {
e.printStackTrace();
}
}
@Override
public void OnRTMPCHttpFailed(int i) {
ToastUtil.show("获取列表失败");
if (swipeRefresh != null) {
swipeRefresh.setRefreshing(false);
}
}
});
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case org.ar.rtmpc.R.id.btn_audio:
if (AndPermission.hasPermissions(LiveListActivity.this,Permission.CAMERA,Permission.RECORD_AUDIO)){
Intent intent = new Intent(LiveListActivity.this, AudioHosterActivity.class);
intent.putExtra("pushURL", DeveloperInfo.PUSH_URL);
intent.putExtra("pullURL", DeveloperInfo.PULL_URL);
startActivity(intent);
}else {
PermissionsCheckUtil.showMissingPermissionDialog(LiveListActivity.this, "请先开启录音和相机权限");
}
break;
case org.ar.rtmpc.R.id.btn_video:
if (AndPermission.hasPermissions(LiveListActivity.this,Permission.CAMERA,Permission.RECORD_AUDIO)){
Intent intent = new Intent(LiveListActivity.this, HosterActivity.class);
intent.putExtra("pushURL", DeveloperInfo.PUSH_URL);
intent.putExtra("pullURL", DeveloperInfo.PULL_URL);
startActivity(intent);
}else {
PermissionsCheckUtil.showMissingPermissionDialog(LiveListActivity.this, "请先开启录音和相机权限");
}
break;
}
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
System.exit(0);
finishAnimActivity();
return true;
}
return super.onKeyDown(keyCode, event);
}
}
================================================
FILE: app/src/main/java/org/ar/hoster/AudioHosterActivity.java
================================================
package org.ar.hoster;
import android.content.DialogInterface;
import android.media.AudioManager;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.support.v7.app.AlertDialog;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.ImageButton;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.chad.library.adapter.base.BaseQuickAdapter;
import org.ar.BaseActivity;
import org.ar.ARApplication;
import org.ar.adapter.AudioLineAdapter;
import org.ar.adapter.LiveMessageAdapter;
import org.ar.adapter.LogAdapter;
import org.ar.common.utils.ARAudioManager;
import org.ar.rtmpc.R;
import org.ar.model.LineBean;
import org.ar.model.MessageBean;
import org.ar.utils.ARUtils;
import org.ar.utils.DisplayUtils;
import org.ar.utils.ToastUtil;
import org.ar.widgets.CustomDialog;
import org.ar.widgets.KeyboardDialogFragment;
import org.ar.common.enums.ARVideoCommon;
import org.ar.rtmpc_hybrid.ARRtmpcEngine;
import org.ar.rtmpc_hybrid.ARRtmpcHosterEvent;
import org.ar.rtmpc_hybrid.ARRtmpcHosterKit;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
/**
* 音频主播页面
*/
public class AudioHosterActivity extends BaseActivity implements BaseQuickAdapter.OnItemChildClickListener {
TextView tvTitle, tvRtmpOk, tvRtmpStatus, tvRtcOk, tvMemberNum, tv_host_name;
RecyclerView rvMsgList;
View viewSpace;
ImageButton tvLineList;
RecyclerView rvLineList,rvLog;
RelativeLayout rl_log_layout;
private LogAdapter logAdapter;
private AudioLineAdapter audioLineAdapter;
private ARRtmpcHosterKit mHosterKit;
private ARAudioManager mRtmpAudioManager = null;
private LiveMessageAdapter mAdapter;
private String nickname;
private CustomDialog line_dialog;
private LineFragment lineFragment;
private boolean isShowLineList = false;
HosterActivity.LineListener lineListener;
private String pushURL = "",pullURL="", liveId = ARApplication.LIVE_ID,userId="host"+(int)((Math.random()*9+1)*100000)+"";
private List applyLineList=new ArrayList<>();//申请连麦的人 这个用于判断小红点显示隐藏
@Override
protected void onResume() {
super.onResume();
}
@Override
protected void onPause() {
super.onPause();
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
ShowExitDialog();
}
return false;
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mHosterKit != null) {
mHosterKit.clean();
mHosterKit = null;
}
// Close RTMPAudioManager
if (mRtmpAudioManager != null) {
mRtmpAudioManager.stop();
mRtmpAudioManager = null;
}
}
@Override
public int getLayoutId() {
return R.layout.activity_audio_hoster;
}
@Override
public void initView(Bundle savedInstanceState) {
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
tvTitle = findViewById(R.id.tv_title);
rl_log_layout=findViewById(R.id.rl_log_layout);
rvLog=findViewById(R.id.rv_log);
tvRtmpOk = findViewById(R.id.tv_rtmp_ok);
tvRtmpStatus = findViewById(R.id.tv_rtmp_status);
tvRtcOk = findViewById(R.id.tv_rtc_ok);
rvMsgList = findViewById(R.id.rv_msg_list);
viewSpace = findViewById(R.id.view_space);
mImmersionBar.titleBar(viewSpace).init();
tvMemberNum = findViewById(R.id.tv_member_num);
tvLineList = findViewById(R.id.tv_line_list);
rvLineList = findViewById(R.id.rv_line_list);
tv_host_name = findViewById(R.id.tv_host_name);
initLineFragment();
logAdapter = new LogAdapter();
rvLog.setLayoutManager(new LinearLayoutManager(this));
logAdapter.bindToRecyclerView(rvLog);
mAdapter = new LiveMessageAdapter();
audioLineAdapter = new AudioLineAdapter(true);
audioLineAdapter.setOnItemChildClickListener(this);
rvLineList.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false));
rvLineList.setAdapter(audioLineAdapter);
rvMsgList.setLayoutManager(new LinearLayoutManager(this));
rvMsgList.setAdapter(mAdapter);
pushURL = getIntent().getStringExtra("pushURL");
pullURL=getIntent().getStringExtra("pullURL");
tvTitle.setText("房间ID:" + liveId);
nickname = ARApplication.getNickName();
tv_host_name.setText(nickname);
mRtmpAudioManager = ARAudioManager.create(this);
mRtmpAudioManager.start(new ARAudioManager.AudioManagerEvents() {
@Override
public void onAudioDeviceChanged(ARAudioManager.AudioDevice audioDevice, Set set) {
}
});
ARRtmpcEngine.Inst().getHosterOption().setMediaType(ARVideoCommon.ARMediaType.Audio);
mHosterKit = new ARRtmpcHosterKit(mHosterListener);
mHosterKit.startPushRtmpStream(pushURL);
mHosterKit.createRTCLine("", liveId, "host", getUserData(), getLiveInfo(pullURL, pullURL));
}
public String getLiveInfo(String pullUrl, String hlsUrl) {
JSONObject liveInfo = new JSONObject();
try {
liveInfo.put("hosterId", userId);
liveInfo.put("rtmpUrl", pullUrl);
liveInfo.put("hlsUrl", hlsUrl);
liveInfo.put("liveTopic", liveId);
liveInfo.put("anyrtcId", liveId);
liveInfo.put("isAudioLive", 1);
liveInfo.put("hosterName", nickname);
} catch (JSONException e) {
e.printStackTrace();
}
return liveInfo.toString();
}
public String getUserData() {
JSONObject user = new JSONObject();
try {
user.put("isHost", 1);
user.put("userId", userId);
user.put("nickName", ARApplication.getNickName());
user.put("headUrl", "www.baidu.com");
} catch (JSONException e) {
e.printStackTrace();
}
return user.toString();
}
private void onAudioManagerChangedState() {
// TODO(henrika): disable video if
// AppRTCAudioManager.AudioDevice.EARPIECE
// is active.
setVolumeControlStream(AudioManager.STREAM_VOICE_CALL);
}
/**
* 更细列表
*
* @param chatMessageBean
*/
private void addChatMessageList(MessageBean chatMessageBean) {
// 150 条 修改;
if (chatMessageBean == null) {
return;
}
if (mAdapter.getData().size() < 150) {
mAdapter.addData(chatMessageBean);
} else {
mAdapter.remove(0);
mAdapter.addData(chatMessageBean);
}
rvMsgList.smoothScrollToPosition(mAdapter.getData().size() - 1);
}
private void showChatLayout() {
KeyboardDialogFragment keyboardDialogFragment = new KeyboardDialogFragment();
keyboardDialogFragment.show(getSupportFragmentManager(), "KeyboardDialogFragment");
keyboardDialogFragment.setEdittextListener(new KeyboardDialogFragment.EdittextListener() {
@Override
public void setTextStr(String text) {
addChatMessageList(new MessageBean(MessageBean.AUDIO, nickname, text));
mHosterKit.sendMessage(0, nickname, "", text);
}
@Override
public void dismiss(DialogFragment dialogFragment) {
}
});
}
private void ShowExitDialog() {
AlertDialog.Builder build = new AlertDialog.Builder(this);
build.setTitle(R.string.str_exit);
build.setMessage(R.string.str_live_stop);
build.setPositiveButton(R.string.str_ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// TODO Auto-generated method stub
finishAnimActivity();
}
});
build.setNegativeButton(R.string.str_cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// TODO Auto-generated method stub
}
});
build.show();
}
public void printLog(String log){
Log.d("RTMPC", log);
logAdapter.addData(log);
}
/**
* 主播回调信息接口
*/
private ARRtmpcHosterEvent mHosterListener = new ARRtmpcHosterEvent() {
/**
* rtmp连接成功
*/
@Override
public void onRtmpStreamOk() {
AudioHosterActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
printLog("回调:onRtmpStreamOk");
if (tvRtmpOk != null) {
tvRtmpOk.setText("RTMP连接成功");
}
}
});
}
/**
* rtmp 重连次数
* @param times 重连次数
*/
@Override
public void onRtmpStreamReconnecting(final int times) {
AudioHosterActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
printLog("回调:onRtmpStreamReconnecting times:" + times);
}
});
}
/**
* rtmp 推流状态
* @param delayMs 推流延时
* @param netBand 推流码流
*/
@Override
public void onRtmpStreamStatus(final int delayMs, final int netBand) {
AudioHosterActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
printLog("回调:onRtmpStreamStatus delayMs:" + delayMs + "netBand:" + netBand);
if (tvRtmpStatus != null) {
tvRtmpStatus.setText(String.format(getString(R.string.str_rtmp_status), delayMs + "ms", netBand / 1024 / 8 + "kb/s"));
}
}
});
}
/**
* rtmp推流失败回调
* @param code
*/
@Override
public void onRtmpStreamFailed(final int code) {
AudioHosterActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
printLog("回调:onRtmpStreamFailed code:" + code);
if (tvRtmpStatus != null) {
tvRtmpStatus.setText("推流失败");
}
}
});
}
/**
* rtmp 推流关闭回调
*/
@Override
public void onRtmpStreamClosed() {
Log.d("RTMPC", "onRtmpStreamClosed ");
AudioHosterActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
printLog("回调:onRtmpStreamClosed ");
if (tvRtmpStatus != null) {
tvRtmpStatus.setText("RTMP流关闭");
}
}
});
}
/**
* RTC 连接回调
* @param code 0: 连接成功
*/
@Override
public void onRTCCreateLineResult(final int code, String s) {
AudioHosterActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
printLog("回调: onRTCCreateLineResult code:" + code);
if (code == 0) {
if (tvRtcOk != null) {
tvRtcOk.setText(R.string.str_rtc_connect_success);
}
} else {
if (tvRtcOk != null) {
tvRtcOk.setText(ARUtils.getErrString(code));
}
}
}
});
}
/**
* 游客有申请连线回调
*
* @param strLivePeerID
* @param strCustomID
* @param strUserData
*/
@Override
public void onRTCApplyToLine(final String strLivePeerID, final String strCustomID, final String strUserData) {
AudioHosterActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
printLog("回调:onRTCApplyToLine strLivePeerID:" + strLivePeerID + " strCustomID:" + strCustomID + " strUserData:" + strUserData);
try {
JSONObject jsonObject = new JSONObject(strUserData);
if (line_dialog != null && lineListener != null && mHosterKit != null) {
lineListener.AddAudioGuest(new LineBean(strLivePeerID, jsonObject.getString("nickName"), false), mHosterKit);
tvLineList.setSelected(true);
}
applyLineList.add(strLivePeerID);
} catch (JSONException e) {
e.printStackTrace();
}
}
});
}
/**
* 游客挂断连线回调
* @param strLivePeerID
*/
@Override
public void onRTCCancelLine(final int nCode, final String strLivePeerID) {
AudioHosterActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
printLog("回调:onRTCCancelLine strLivePeerID:" + strLivePeerID + "nCode:" + nCode);
if (nCode == 602) {
ToastUtil.show("连麦人数已满");
}
if (nCode == 0) {
if (line_dialog != null && lineListener != null) {
lineListener.RemoveGuest(strLivePeerID);
}
if (applyLineList.contains(strLivePeerID)) {
applyLineList.remove(strLivePeerID);
}
if (applyLineList.size()==0){//小红点
tvLineList.setSelected(false);
}
}
}
});
}
@Override
public void onRTCOpenRemoteVideoRender(String s, String s1, String s2, String s3) {
}
@Override
public void onRTCCloseRemoteVideoRender(String s, String s1, String s2) {
}
@Override
public void onRTCOpenRemoteAudioLine(final String strLivePeerId, final String strUserId, final String strUserData) {
AudioHosterActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
printLog("回调: onRTCOpenRemoteAudioLine strLivePeerID:" + strLivePeerId + " strUserId:" + strUserId + " strUserData:" + strUserData);
try {
JSONObject jsonObject = new JSONObject(strUserData);
audioLineAdapter.addData(new LineBean(strLivePeerId, jsonObject.getString("nickName"), false));
} catch (JSONException e) {
e.printStackTrace();
}
}
});
}
@Override
public void onRTCCloseRemoteAudioLine(final String strLivePeerId, final String strUserId) {
AudioHosterActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
printLog("回调: onRTCCloseRemoteAudioLine strLivePeerID:" + strLivePeerId + " strUserId:" + strUserId);
int index = 9;
for (int i = 0; i < audioLineAdapter.getData().size(); i++) {
if (audioLineAdapter.getItem(i).peerId.equals(strLivePeerId)) {
index = i;
}
}
if (index != 9 && index <= audioLineAdapter.getData().size()) {
audioLineAdapter.remove(index);
}
}
});
}
@Override
public void onRTCLocalAudioActive(final int leave) {
AudioHosterActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
Log.d("RTMPC", "onRTLocalAudioActive leave:" + leave);
}
});
}
@Override
public void onRTCRemoteAudioActive(final String strLivePeerId, final String strUserId, final int nTime) {
AudioHosterActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
Log.d("RTMPC", "onRTCAudioActive strLivePeerID:" + strLivePeerId + " strUserId:" + strUserId + " nTime:" + nTime);
if (strLivePeerId.equals("RTMPC_Hoster")) {//主播
} else {
// for (int i = 0; i < audioLineAdapter.getData().size(); i++) {
// if (strLivePeerId.equals(audioLineAdapter.getData().get(i).peerId)) {
// audioLineAdapter.getItem(i).setStartAnim(true);
// audioLineAdapter.notifyItemChanged(i);
// }
// }
}
}
});
}
@Override
public void onRTCRemoteAVStatus(final String s, boolean b, boolean b1) {
AudioHosterActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
printLog("回调:onRTCRemoteAVStatus peerID:"+s);
}
});
}
/**
* RTC 连接关闭回调
* @param code 207:请去AnyRTC官网申请账号,如有疑问请联系客服!
* @param strReason
*/
/**
* RTC 连接关闭回调
* @param code 207:请去AnyRTC官网申请账号,如有疑问请联系客服!
*/
@Override
public void onRTCLineClosed(final int code, String s) {
AudioHosterActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
printLog("回调:onRTCLineClosedLine code:" + code);
}
});
}
/**
* 消息回调
* @param strCustomID 消息的发送者id
* @param strCustomName 消息的发送者昵称
* @param strCustomHeader 消息的发送者头像url
* @param strMessage 消息内容
*/
@Override
public void onRTCUserMessage(final int nType, final String strCustomID, final String strCustomName, final String strCustomHeader, final String strMessage) {
AudioHosterActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
printLog("回调:onRTCUserMessage nType:" + nType + " strUserId:" + strCustomID + " strCustomName:" + strCustomName + " strCustomHeader:" + strCustomHeader + " strMessage:" + strMessage);
addChatMessageList(new MessageBean(MessageBean.AUDIO, strCustomName, strMessage));
}
});
}
/**
* 观看直播的总人数回调
* @param totalMembers 观看直播的总人数
*/
@Override
public void onRTCMemberNotify(final String strServerId, final String strRoomId, final int totalMembers) {
AudioHosterActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
printLog("回调:onRTCMemberNotify strServerId:" + strServerId + "strRoomId:" + strRoomId + "totalMembers:" + totalMembers);
tvMemberNum.setText("在线观看人数" + totalMembers + "");
}
});
}
};
public void onClick(View view) {
switch (view.getId()) {
case R.id.btn_close:
ShowExitDialog();
break;
case R.id.iv_message:
showChatLayout();
break;
case R.id.tv_line_list:
if (isShowLineList) {
if (line_dialog != null) {
line_dialog.hide();
isShowLineList = false;
}
} else {
if (line_dialog != null) {
line_dialog.show();
tvLineList.setSelected(false);
isShowLineList = true;
}
}
break;
case R.id.btn_log:
rl_log_layout.setVisibility(View.VISIBLE);
break;
case R.id.ibtn_close_log:
rl_log_layout.setVisibility(View.GONE);
break;
}
}
private void initLineFragment() {
CustomDialog.Builder builder = new CustomDialog.Builder(this);
builder.setContentView(R.layout.item_line_list)
.setAnimId(R.style.AnimBottom)
.setGravity(Gravity.BOTTOM)
.setLayoutParams(WindowManager.LayoutParams.MATCH_PARENT, DisplayUtils.getScreenHeightPixels(this) / 3)
.setBackgroundDrawable(true)
.build();
line_dialog = builder.show(new CustomDialog.Builder.onInitListener() {
@Override
public void init(CustomDialog view) {
if (lineFragment == null) {
lineFragment = new LineFragment();
}
}
});
line_dialog.hide();
line_dialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
isShowLineList = false;
}
});
}
public void SetLineListener(HosterActivity.LineListener mLineListener) {
this.lineListener = mLineListener;
}
@Override
public void onItemChildClick(BaseQuickAdapter adapter, View view, int position) {
if (mHosterKit != null) {
mHosterKit.hangupRTCLine(audioLineAdapter.getItem(position).peerId);
audioLineAdapter.remove(position);
}
}
}
================================================
FILE: app/src/main/java/org/ar/hoster/HosterActivity.java
================================================
package org.ar.hoster;
import android.content.DialogInterface;
import android.media.AudioManager;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.support.v7.app.AlertDialog;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import org.ar.BaseActivity;
import org.ar.ARApplication;
import org.ar.adapter.LiveMessageAdapter;
import org.ar.adapter.LogAdapter;
import org.ar.common.utils.ARAudioManager;
import org.ar.rtmpc.R;
import org.ar.model.LineBean;
import org.ar.model.MessageBean;
import org.ar.utils.ARUtils;
import org.ar.utils.DisplayUtils;
import org.ar.utils.ToastUtil;
import org.ar.widgets.ARVideoView;
import org.ar.widgets.CustomDialog;
import org.ar.widgets.KeyboardDialogFragment;
import org.ar.common.enums.ARVideoCommon;
import org.ar.rtmpc_hybrid.ARRtmpcEngine;
import org.ar.rtmpc_hybrid.ARRtmpcHosterEvent;
import org.ar.rtmpc_hybrid.ARRtmpcHosterKit;
import org.json.JSONException;
import org.json.JSONObject;
import org.webrtc.VideoRenderer;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
/**
* 视频主播页面
*/
public class HosterActivity extends BaseActivity {
RelativeLayout rlRtmpcVideos,rl_log_layout;
TextView tvTitle,tvRtmpOk,tvRtmpStatus,tvRtcOk,tvMemberNum;
RecyclerView rvMsgList,rvLog;
View viewSpace;
ImageButton tvLineList;
private ARRtmpcHosterKit mHosterKit;
private ARVideoView mVideoView;
private ARAudioManager mRtmpAudioManager;
private LiveMessageAdapter mAdapter;
private LogAdapter logAdapter;
private CustomDialog line_dialog;
private LineFragment lineFragment;
private boolean isShowLineList = false;
private LineListener lineListener;
private String pushURL = "",pullURL="";
private String liveId= ARApplication.LIVE_ID;
private String nickname= ARApplication.getNickName();
private String userId="host"+(int)((Math.random()*9+1)*100000)+"";
private List applyLineList=new ArrayList<>();//申请连麦的人 这个用于判断小红点显示隐藏
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
ShowExitDialog();
}
return false;
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mHosterKit != null) {
mVideoView.removeLocalVideoRender();
mHosterKit.clean();
}
// Close RTMPAudioManager
if (mRtmpAudioManager != null) {
mRtmpAudioManager.stop();
mRtmpAudioManager = null;
}
}
@Override
public int getLayoutId() {
return R.layout.activity_hoster;
}
@Override
public void initView(Bundle savedInstanceState) {
//设置屏幕常亮
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
rlRtmpcVideos=findViewById(R.id.rl_rtmpc_videos);
rl_log_layout=findViewById(R.id.rl_log_layout);
rvLog=findViewById(R.id.rv_log);
tvTitle = findViewById(R.id.tv_title);
tvRtmpOk = findViewById(R.id.tv_rtmp_ok);
tvRtmpStatus = findViewById(R.id.tv_rtmp_status);
tvRtcOk = findViewById(R.id.tv_rtc_ok);
rvMsgList = findViewById(R.id.rv_msg_list);
viewSpace=findViewById(R.id.view_space);
mImmersionBar.titleBar(viewSpace).init();
tvMemberNum=findViewById(R.id.tv_member_num);
tvLineList=findViewById(R.id.tv_line_list);
pushURL = getIntent().getStringExtra("pushURL");
pullURL=getIntent().getStringExtra("pullURL");
initLineFragment();
mAdapter = new LiveMessageAdapter();
rvMsgList.setLayoutManager(new LinearLayoutManager(this));
rvMsgList.setAdapter(mAdapter);
logAdapter = new LogAdapter();
rvLog.setLayoutManager(new LinearLayoutManager(this));
logAdapter.bindToRecyclerView(rvLog);
//设置视频质量 参数对应清晰度,可查看API文档
ARRtmpcEngine.Inst().getHosterOption().setVideoProfile(ARVideoCommon.ARVideoProfile.ARVideoProfile480x640);
tvTitle.setText("房间ID:" + liveId);
ARRtmpcEngine.Inst().getHosterOption().setVideoOrientation(ARVideoCommon.ARVideoOrientation.Portrait);
//音频管理对象 当靠近听筒时将会减小音量
mRtmpAudioManager = ARAudioManager.create(this);
mRtmpAudioManager.start(new ARAudioManager.AudioManagerEvents() {
@Override
public void onAudioDeviceChanged(ARAudioManager.AudioDevice audioDevice, Set set) {
}
});
ARRtmpcEngine.Inst().getHosterOption().setMediaType(ARVideoCommon.ARMediaType.Video);
//实例化主播对象
mHosterKit = new ARRtmpcHosterKit(mHosterListener);
mHosterKit.setAudioActiveCheck(true);
//实例化连麦窗口对象
mVideoView = new ARVideoView(rlRtmpcVideos, ARRtmpcEngine.Inst().egl(), this,false,true);
mVideoView.setVideoViewLayout(false,Gravity.RIGHT,LinearLayout.VERTICAL);
mVideoView.setVideoLayoutOnclickEvent(mBtnVideoCloseEvent);
//设置本地视频采集
VideoRenderer render = mVideoView.openLocalVideoRender();
mHosterKit.setLocalVideoCapturer(render.GetRenderPointer());
//开始推流
mHosterKit.startPushRtmpStream(pushURL);
//创建RTC连接,必须放在开始推流之后
mHosterKit.createRTCLine("", liveId, userId, getUserData(), getLiveInfo(pullURL,pullURL));
//设置音频连麦直播,默认视频
//=====================================视频数据相关===============================
// /**
// * 设置是否采用ARCamera,默认使用ARCamera, 如果设置为false,必须调用setByteBufferFrameCaptured才能本地显示
// * @param usedARCamera true:使用ARCamera,false:不使用ARCamera采集的数据
// */
// mHosterKit.setUsedARCamera(true);
// /**
// * 设置本地显示的视频数据
// * @param data 相机采集数据
// * @param width 宽
// * @param height 高
// * @param rotation 旋转角度
// * @param timeStamp 时间戳
// */
// mHosterKit.setByteBufferFrameCaptured();
//设置ARCamera视频回调数据
// mHosterKit.setARCameraCaptureObserver(new VideoCapturer.ARCameraCapturerObserver() {
// @Override
// public void onByteBufferFrameCaptured(byte[] data, int width, int height, int rotation, long timeStamp) {
// }
// });
//=====================================视频数据相关===============================
}
public String getLiveInfo(String pullUrl,String hlsUrl) {
JSONObject liveInfo = new JSONObject();
try {
liveInfo.put("hosterId", userId);
liveInfo.put("rtmpUrl", pullUrl);
liveInfo.put("hlsUrl", hlsUrl);
liveInfo.put("liveTopic", ARApplication.LIVE_ID);
liveInfo.put("anyrtcId", ARApplication.LIVE_ID);
liveInfo.put("isAudioLive", 0);
liveInfo.put("hosterName", ARApplication.getNickName());
} catch (JSONException e) {
e.printStackTrace();
}
return liveInfo.toString();
}
public String getUserData() {
JSONObject user = new JSONObject();
try {
user.put("isHost", 1);
user.put("userId", userId);
user.put("nickName", ARApplication.getNickName());
user.put("headUrl", "www.baidu.com");
} catch (JSONException e) {
e.printStackTrace();
}
return user.toString();
}
private void onAudioManagerChangedState() {
// TODO(henrika): disable video if
// AppRTCAudioManager.AudioDevice.EARPIECE
// is active.
setVolumeControlStream(AudioManager.STREAM_VOICE_CALL);
}
/**
* 连线时小图标的关闭按钮
*/
private ARVideoView.VideoLayoutOnclickEvent mBtnVideoCloseEvent = new ARVideoView.VideoLayoutOnclickEvent() {
@Override
public void onCloseVideoRender(View view, String strPeerId) {
mHosterKit.hangupRTCLine(strPeerId);
}
};
/**
* 更细列表
*
* @param chatMessageBean
*/
private void addChatMessageList(MessageBean chatMessageBean) {
// 150 条 修改;
if (chatMessageBean == null) {
return;
}
if (mAdapter.getData().size() < 150) {
mAdapter.addData(chatMessageBean);
} else {
mAdapter.remove(0);
mAdapter.addData(chatMessageBean);
}
rvMsgList.smoothScrollToPosition(mAdapter.getData().size() - 1);
}
private void showChatLayout() {
KeyboardDialogFragment keyboardDialogFragment = new KeyboardDialogFragment();
keyboardDialogFragment.show(getSupportFragmentManager(), "KeyboardDialogFragment");
keyboardDialogFragment.setEdittextListener(new KeyboardDialogFragment.EdittextListener() {
@Override
public void setTextStr(String text) {
addChatMessageList(new MessageBean(MessageBean.VIDEO, nickname, text));
mHosterKit.sendMessage(0, nickname, "", text);
}
@Override
public void dismiss(DialogFragment dialogFragment) {
}
});
}
private void ShowExitDialog() {
AlertDialog.Builder build = new AlertDialog.Builder(this);
build.setTitle(R.string.str_exit);
build.setMessage(R.string.str_live_stop);
build.setPositiveButton(R.string.str_ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// TODO Auto-generated method stub
if (mHosterKit != null) {
mHosterKit.stopRtmpStream();
}
finishAnimActivity();
}
});
build.setNegativeButton(R.string.str_cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// TODO Auto-generated method stub
}
});
build.show();
}
public void printLog(String log){
Log.d("RTMPC", log);
logAdapter.addData(log);
}
/**
* 主播回调信息接口
*/
private ARRtmpcHosterEvent mHosterListener = new ARRtmpcHosterEvent() {
/**
* rtmp连接成功
*/
@Override
public void onRtmpStreamOk() {
HosterActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
printLog("回调:onRtmpStreamOk");
if (tvRtmpOk != null) {
tvRtmpOk.setText("Rtmp连接成功");
}
}
});
}
/**
* rtmp 重连次数
* @param times 重连次数
*/
@Override
public void onRtmpStreamReconnecting(final int times) {
HosterActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
printLog("回调:onRtmpStreamReconnecting times:" + times);
if (tvRtmpStatus != null) {
tvRtmpStatus.setText(String.format(getString(R.string.str_reconnect_times), times));
}
}
});
}
/**
* rtmp 推流状态
* @param delayMs 推流延时
* @param netBand 推流码流
*/
@Override
public void onRtmpStreamStatus(final int delayMs, final int netBand) {
HosterActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
printLog("回调:onRtmpStreamStatus delayMs:" + delayMs + "netBand:" + netBand);
if (tvRtmpStatus != null) {
tvRtmpStatus.setText(String.format(getString(R.string.str_rtmp_status), delayMs + "ms", netBand / 1024 / 8 + "kb/s"));
}
}
});
}
/**
* rtmp推流失败回调
* @param code
*/
@Override
public void onRtmpStreamFailed(final int code) {
HosterActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
printLog("回调:onRtmpStreamFailed code:" + code);
if (tvRtcOk != null) {
tvRtcOk.setText(R.string.str_rtmp_connect_failed);
}
}
});
}
/**
* rtmp 推流关闭回调
*/
@Override
public void onRtmpStreamClosed() {
HosterActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
printLog("回调:onRtmpStreamClosed ");
finish();
}
});
}
/**
* RTC 连接回调
* @param code 0: 连接成功
*/
@Override
public void onRTCCreateLineResult(final int code, String s) {
HosterActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
printLog("回调: onRTCCreateLineResult code:" + code);
if (tvRtcOk != null) {
if (code == 0) {
tvRtcOk.setText(R.string.str_rtc_connect_success);
} else {
tvRtcOk.setText(ARUtils.getErrString(code));
}
}
}
});
}
/**
* 游客有申请连线回调
*
* @param strLivePeerID
* @param strCustomID
* @param strUserData
*/
@Override
public void onRTCApplyToLine(final String strLivePeerID, final String strCustomID, final String strUserData) {
HosterActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
printLog("回调:onRTCApplyToLine strLivePeerID:" + strLivePeerID + " strCustomID:" + strCustomID + " strUserData:" + strUserData);
try {
String userdata = URLDecoder.decode(strUserData);
JSONObject jsonObject = new JSONObject(userdata);
if (line_dialog != null && lineListener != null && mHosterKit != null && tvLineList != null) {
lineListener.AddGuest(new LineBean(strLivePeerID, jsonObject.getString("nickName"), false), mHosterKit);
tvLineList.setSelected(true);
}
applyLineList.add(strLivePeerID);
} catch (JSONException e) {
e.printStackTrace();
}
}
});
}
/**
* 游客挂断连线回调
* @param strLivePeerID
*/
@Override
public void onRTCCancelLine(final int nCode, final String strLivePeerID) {
HosterActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
printLog("回调:onRTCCancelLine strLivePeerID:" + strLivePeerID + "nCode:" + nCode);
if (nCode == 0) {
if (line_dialog != null && lineListener != null) {
lineListener.RemoveGuest(strLivePeerID);
}
if (applyLineList.contains(strLivePeerID)) {
applyLineList.remove(strLivePeerID);
}
if (applyLineList.size()==0){//小红点
tvLineList.setSelected(false);
}
}
if (nCode == 602) {
ToastUtil.show("连麦人数已满");
}
}
});
}
@Override
public void onRTCOpenRemoteVideoRender(final String strLivePeerId, final String strPublishId, final String strUserId, final String strUserData) {
HosterActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
printLog("回调:onRTCOpenVideoRender strPublishId:" + strPublishId + " strUserId:" + strUserId + " strUserData:" + strUserData);
final VideoRenderer render = mVideoView.openRemoteVideoRender(strLivePeerId);
if (null != render) {
mHosterKit.setRTCRemoteVideoRender(strPublishId, render.GetRenderPointer());
}
}
});
}
@Override
public void onRTCCloseRemoteVideoRender(final String strLivePeerId, final String strPublishId, final String strUserId) {
HosterActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
printLog("回调:onRTCCloseVideoRender strPublishId:" + strPublishId + " strUserId:" + strUserId);
mHosterKit.setRTCRemoteVideoRender(strPublishId, 0);
mVideoView.removeRemoteRender(strLivePeerId);
if (line_dialog != null && lineListener != null) {
lineListener.RemoveGuest(strLivePeerId);
}
}
});
}
@Override
public void onRTCOpenRemoteAudioLine(String s, String s1, String s2) {
}
@Override
public void onRTCCloseRemoteAudioLine(String s, String s1) {
HosterActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
}
});
}
@Override
public void onRTCLocalAudioActive(int i) {
HosterActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
Log.d("RTMPC", "onRTLocalAudioActive ");
}
});
}
@Override
public void onRTCRemoteAudioActive(String s, String s1, int i) {
HosterActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
Log.d("RTMPC", "onRTCRemoteAudioActive ");
}
});
}
@Override
public void onRTCRemoteAVStatus(final String s, boolean b, boolean b1) {
HosterActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
printLog("回调:onRTCRemoteAVStatus peerID:"+s);
}
});
}
/**
* RTC 连接关闭回调
* @param code 207:请去AnyRTC官网申请账号,如有疑问请联系客服!
*/
@Override
public void onRTCLineClosed(final int code, String s) {
HosterActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
Log.d("RTMPC", "onRTCLineClosedLine code:" + code);
if (code == 207) {
Toast.makeText(HosterActivity.this, getString(R.string.str_apply_anyrtc_account), Toast.LENGTH_LONG).show();
finish();
}
}
});
}
/**
* 连线接通时的视频图像回调;
*/
/**
* 连线关闭时的视频图像回调;
*/
/**
* 消息回调
* @param strCustomID 消息的发送者id
* @param strCustomName 消息的发送者昵称
* @param strCustomHeader 消息的发送者头像url
* @param strMessage 消息内容
*/
@Override
public void onRTCUserMessage(final int nType, final String strCustomID, final String strCustomName, final String strCustomHeader, final String strMessage) {
HosterActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
printLog("回调:onRTCUserMessage nType:" + nType + " strUserId:" + strCustomID + " strCustomName:" + strCustomName + " strCustomHeader:" + strCustomHeader + " strMessage:" + strMessage);
addChatMessageList(new MessageBean(MessageBean.VIDEO, strCustomName, strMessage));
}
});
}
/**
* 观看直播的总人数回调
* @param totalMembers 观看直播的总人数
*/
@Override
public void onRTCMemberNotify(final String strServerId, final String strRoomId, final int totalMembers) {
HosterActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
printLog("回调:onRTCMemberNotify strServerId:" + strServerId + "strRoomId:" + strRoomId + "totalMembers:" + totalMembers);
tvMemberNum.setText("在线人数:" + totalMembers + "");
}
});
}
};
public void onClick(View view) {
switch (view.getId()) {
case R.id.btn_camare:
if (mHosterKit == null) {
return;
}
mHosterKit.switchCamera();
break;
case R.id.btn_close:
ShowExitDialog();
break;
case R.id.iv_message:
showChatLayout();
break;
case R.id.tv_line_list:
if (isShowLineList) {
if (line_dialog != null) {
line_dialog.hide();
isShowLineList = false;
}
} else {
if (line_dialog != null) {
line_dialog.show();
tvLineList.setSelected(false);
isShowLineList = true;
}
}
break;
case R.id.btn_log:
rl_log_layout.setVisibility(View.VISIBLE);
break;
case R.id.ibtn_close_log:
rl_log_layout.setVisibility(View.GONE);
break;
}
}
private void initLineFragment() {
CustomDialog.Builder builder = new CustomDialog.Builder(this);
builder.setContentView(R.layout.item_line_list)
.setAnimId(R.style.AnimBottom)
.setGravity(Gravity.BOTTOM)
.setLayoutParams(WindowManager.LayoutParams.MATCH_PARENT, DisplayUtils.getScreenHeightPixels(this) / 3)
.setBackgroundDrawable(true)
.build();
line_dialog = builder.show(new CustomDialog.Builder.onInitListener() {
@Override
public void init(CustomDialog view) {
if (lineFragment == null) {
lineFragment = new LineFragment();
}
}
});
line_dialog.hide();
line_dialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
isShowLineList = false;
}
});
}
public interface LineListener {
void AddAudioGuest(LineBean lineBean, ARRtmpcHosterKit hosterKit);//添加音频游客的申请到列表
void AddGuest(LineBean lineBean, ARRtmpcHosterKit hosterKit);//添加游客的申请到列表
void RemoveGuest(String peerid);//从列表移除游客
}
public void SetLineListener(LineListener mLineListener) {
this.lineListener = mLineListener;
}
public void closeLineDialog() {
if (line_dialog != null) {
line_dialog.hide();
}
}
}
================================================
FILE: app/src/main/java/org/ar/hoster/LineFragment.java
================================================
package org.ar.hoster;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.chad.library.adapter.base.BaseQuickAdapter;
import org.ar.adapter.LiveLineAdapter;
import org.ar.rtmpc.R;
import org.ar.model.LineBean;
import org.ar.rtmpc_hybrid.ARRtmpcHosterKit;
/**
* Created by liuxiaozhong on 2017/9/24.
*/
public class LineFragment extends Fragment implements HosterActivity.LineListener,BaseQuickAdapter.OnItemChildClickListener{
private RecyclerView lineList;
private LiveLineAdapter mAadapter;
private ARRtmpcHosterKit rtmpcHosterKit;
private ARRtmpcHosterKit audioHosterKit;
private HosterActivity activity;
private AudioHosterActivity audioHosterActivity;
private SwipeRefreshLayout swipe_refresh;
private boolean isAudioLive=false;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_line, container, false);
swipe_refresh= (SwipeRefreshLayout) view.findViewById(R.id.swipe_refresh);
swipe_refresh.setEnabled(false);
lineList= (RecyclerView) view.findViewById(R.id.rv_list);
lineList.setLayoutManager(new LinearLayoutManager(getActivity()));
mAadapter=new LiveLineAdapter();
mAadapter.setEmptyView(R.layout.empty_no_line_data, (ViewGroup) lineList.getParent());
mAadapter.setOnItemChildClickListener(this);
lineList.setAdapter(mAadapter);
if (getActivity() instanceof HosterActivity){
isAudioLive=false;
activity= (HosterActivity) getActivity();
activity.SetLineListener(this);
}else {
isAudioLive=true;
audioHosterActivity= (AudioHosterActivity) getActivity();
audioHosterActivity.SetLineListener(this);
}
return view;
}
@Override
public void AddAudioGuest(LineBean lineBean, ARRtmpcHosterKit hosterKit) {
isAudioLive=true;
this.audioHosterKit=hosterKit;
mAadapter.addData(lineBean);
}
@Override
public void AddGuest(LineBean lineBean, ARRtmpcHosterKit hosterKit) {
isAudioLive=false;
this.rtmpcHosterKit=hosterKit;
mAadapter.addData(lineBean);
}
@Override
public void RemoveGuest(String peerid) {
int index=9;
for (int i=0;i
*/
public class ScreenUtils {
private ScreenUtils() {
/* cannot be instantiated */
throw new UnsupportedOperationException("cannot be instantiated");
}
/**
* dip to px
*
* @param dip
* @param context
* @return
*/
public static float dip2Dimension(float dip, Context context) {
DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, displayMetrics);
}
public static int dpToP(Resources paramResources, int paramInt) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, paramInt,
paramResources.getDisplayMetrics());
}
/**
* @param context
* @param complexUnit {@link TypedValue#COMPLEX_UNIT_DIP}
* {@link TypedValue#COMPLEX_UNIT_SP}
* @return
*/
public static float toDimension(float dip, Context context, int complexUnit) {
DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
return TypedValue.applyDimension(complexUnit, dip, displayMetrics);
}
/**
* Get screen width
*
* @param context
* @return
*/
public static int getScreenWidth(Context context) {
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics outMetrics = new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(outMetrics);
return outMetrics.widthPixels;
}
/**
* Get screen height
*
* @param context
* @return
*/
public static int getScreenHeight(Context context) {
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics outMetrics = new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(outMetrics);
return outMetrics.heightPixels;
}
/**
* 判断是否为平板
*
* @return
*/
public static boolean isPad(Context context) {
DisplayMetrics dm = context.getResources().getDisplayMetrics();
int width = dm.widthPixels;
int height = dm.heightPixels;
double x = Math.pow(width, 2);
double y = Math.pow(height, 2);
double diagonal = Math.sqrt(x + y);
int dens = dm.densityDpi;
double screenInches = diagonal / (double) dens;
// 大于6尺寸则为Pad
if (screenInches >= 6.0) {
return true;
}
return false;
}
/**
* 获得状态栏的高度
*
* @param context
* @return
*/
public static int getStatusHeight(Context context) {
int statusHeight = -1;
try {
Class> clazz = Class.forName("com.android.internal.R$dimen");
Object object = clazz.newInstance();
int height = Integer.parseInt(clazz.getField("status_bar_height").get(object).toString());
statusHeight = context.getResources().getDimensionPixelSize(height);
} catch (Exception e) {
e.printStackTrace();
}
return statusHeight;
}
/**
* 获取当前屏幕截图,包含状态栏
*
* @param activity
* @return
*/
public static Bitmap snapShotWithStatusBar(Activity activity) {
View view = activity.getWindow().getDecorView();
view.setDrawingCacheEnabled(true);
view.buildDrawingCache();
Bitmap bmp = view.getDrawingCache();
int width = getScreenWidth(activity);
int height = getScreenHeight(activity);
Bitmap bp = null;
bp = Bitmap.createBitmap(bmp, 0, 0, width, height);
view.destroyDrawingCache();
return bp;
}
/**
* 获取当前屏幕截图,不包含状态栏
*
* @param activity
* @return
*/
public static Bitmap snapShotWithoutStatusBar(Activity activity) {
View view = activity.getWindow().getDecorView();
view.setDrawingCacheEnabled(true);
view.buildDrawingCache();
Bitmap bmp = view.getDrawingCache();
Rect frame = new Rect();
activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(frame);
int statusBarHeight = frame.top;
int width = getScreenWidth(activity);
int height = getScreenHeight(activity);
Bitmap bp = null;
bp = Bitmap.createBitmap(bmp, 0, statusBarHeight, width, height - statusBarHeight);
view.destroyDrawingCache();
return bp;
}
public static final int getHeightInPx(Context context) {
final int height = context.getResources().getDisplayMetrics().heightPixels;
return height;
}
public static final int getWidthInPx(Context context) {
final int width = context.getResources().getDisplayMetrics().widthPixels;
return width;
}
public static final int getHeightInDp(Context context) {
final float height = context.getResources().getDisplayMetrics().heightPixels;
int heightInDp = px2dip(context, height);
return heightInDp;
}
public static final int getWidthInDp(Context context) {
final float height = context.getResources().getDisplayMetrics().heightPixels;
int widthInDp = px2dip(context, height);
return widthInDp;
}
public static int dip2px(Context context, float dpValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
public static int px2dip(Context context, float pxValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (pxValue / scale + 0.5f);
}
public static int px2sp(Context context, float pxValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (pxValue / scale + 0.5f);
}
public static int sp2px(Context context, float spValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (spValue * scale + 0.5f);
}
}
================================================
FILE: app/src/main/java/org/ar/utils/ToastUtil.java
================================================
package org.ar.utils;
import android.content.Context;
import android.text.TextUtils;
import android.view.Gravity;
import android.widget.Toast;
import org.ar.ARApplication;
/**
* Created by Skyline on 2016/5/24.
*/
public class ToastUtil {
private static Context context = ARApplication.App();
private static Toast mToast;
public static void show(int resId) {
show(context.getResources().getText(resId), Toast.LENGTH_SHORT);
}
public static void show(int resId, int duration) {
show(context.getResources().getText(resId), duration);
}
public static void show(CharSequence text) {
show(text, Toast.LENGTH_SHORT);
}
public static void show(CharSequence text, int duration) {
text = TextUtils.isEmpty(text == null ? "" : text.toString()) ? ""
: text;
if (mToast == null) {
mToast = Toast.makeText(context, text, duration);
mToast.setGravity(Gravity.CENTER, 0, 0);
} else {
mToast.setText(text);
}
mToast.show();
}
public static void show(int resId, Object... args) {
show(String.format(context.getResources().getString(resId), args),
Toast.LENGTH_SHORT);
}
public static void show(String format, Object... args) {
show(String.format(format, args), Toast.LENGTH_SHORT);
}
public static void show(int resId, int duration, Object... args) {
show(String.format(context.getResources().getString(resId), args),
duration);
}
public static void show(String format, int duration, Object... args) {
show(String.format(format, args), duration);
}
public void cancelToast() {
if (mToast != null) {
mToast.cancel();
}
}
}
================================================
FILE: app/src/main/java/org/ar/widgets/ARVideoView.java
================================================
package org.ar.widgets;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.util.Log;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import org.ar.common.utils.ScreenUtils;
import org.ar.rtmpc.R;
import org.webrtc.EglBase;
import org.webrtc.EglRenderer;
import org.webrtc.PercentFrameLayout;
import org.webrtc.RendererCommon;
import org.webrtc.SurfaceViewRenderer;
import org.webrtc.VideoRenderer;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import static android.view.View.VISIBLE;
/**
* Created by liuxiaozhong on 2019/1/11.
*/
public class ARVideoView implements View.OnTouchListener{
public RelativeLayout rlVideoGroup;//所有视频的容器布局
private EglBase eglBase;//底层视频渲染相关对象
private Context mContext;//上下文对象
private VideoView LocalVideoRender;//本地视频显示对象
private LinkedHashMap mRemoteRenderList;//远程视频集合
private static int mScreenWidth;//屏幕宽
private static int mScreenHeight;//屏幕高
private boolean isSameSize=false;//是否是平均大小模式
private boolean is169 = false;//比例是否是16:9
private int direction = Gravity.CENTER;//1大几小的时候 小像位置
private int orientation = LinearLayout.HORIZONTAL;//1大几小的时候 小像横向或纵向排列
private static int SUB_WIDTH = 0;
private static int SUB_HEIGHT = 0;
private boolean isHost ;//是否是主播
VideoLayoutOnclickEvent videoLayoutOnclickEvent;
public interface VideoLayoutOnclickEvent {
void onCloseVideoRender(View view, String strPeerId);
}
public void setVideoLayoutOnclickEvent(VideoLayoutOnclickEvent videoLayoutOnclickEvent) {
this.videoLayoutOnclickEvent = videoLayoutOnclickEvent;
}
public ARVideoView(RelativeLayout rlVideoGroup, EglBase eglBase, Context context, boolean isSameSize, boolean isHost) {
this.rlVideoGroup = rlVideoGroup;
this.eglBase = eglBase;
this.mContext = context;
this.isSameSize=isSameSize;
this.isHost=isHost;
mRemoteRenderList = new LinkedHashMap<>();
mScreenWidth = ScreenUtils.getScreenWidth(mContext);
mScreenHeight = ScreenUtils.getScreenHeight(mContext) - ScreenUtils.getStatusHeight(mContext);
}
public void setVideoSwitchEnable(boolean enable) {
if (!isSameSize) {
rlVideoGroup.setOnTouchListener(this);
}
}
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
int startX = (int) event.getX();
int startY = (int) event.getY();
if (LocalVideoRender.Hited(startX, startY)) {
return true;
} else {
Iterator> iter = mRemoteRenderList.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry entry = iter.next();
String peerId = entry.getKey();
VideoView render = entry.getValue();
if (render.Hited(startX, startY)) {
return true;
}
}
}
} else if (event.getAction() == MotionEvent.ACTION_UP) {
int startX = (int) event.getX();
int startY = (int) event.getY();
if (LocalVideoRender.Hited(startX, startY)) {
SwitchViewToFullscreen(LocalVideoRender, GetFullScreen());
return true;
} else {
Iterator> iter = mRemoteRenderList.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry entry = iter.next();
String peerId = entry.getKey();
VideoView render = entry.getValue();
if (render.Hited(startX, startY)) {
SwitchViewToFullscreen(render, GetFullScreen());
return true;
}
}
}
}
return false;
}
/**
* 一个VideoView对象 就是一个视频渲染对象 里面的方法 UI 可以根据需求自定义
*/
protected static class VideoView {
public String videoId; //视频ID 保持唯一
public int index; //视频的下标
public int x; //装载视频的容器的起始X轴位置 最大100 最左边为0
public int y; //装载视频的容器的起始Y轴位置 最大100 最上边为0
public int w; //装载视频的容器的宽 最大100
public int h; //装载视频的容器的高 最大100
public PercentFrameLayout mLayout = null;//自定义宽高为百分比的布局控件
public SurfaceViewRenderer surfaceViewRenderer = null; //显示视频的SurfaceView
private FrameLayout flLoading; //视频显示前的Loading
public VideoRenderer videoRenderer = null; //底层视频渲染对象
public ImageButton btnHangUp;
public VideoView(String videoId, Context ctx, EglBase eglBase, int index, int x, int y, int w, int h) {
this.videoId = videoId;
this.index = index;
this.x = x;
this.y = y;
this.w = w;
this.h = h;
mLayout = new PercentFrameLayout(ctx);
mLayout.setLayoutParams(new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT));
View view = View.inflate(ctx, R.layout.layout_arvideo, null);//这个View可完全自定义 需要显示名字或者其他图标可以在里面加
flLoading = (FrameLayout) view.findViewById(R.id.fl_video_loading);
btnHangUp=view.findViewById(R.id.ibtn_hang_up);
surfaceViewRenderer = (SurfaceViewRenderer) view.findViewById(R.id.sv_video_render);
surfaceViewRenderer.init(eglBase.getEglBaseContext(), null);
surfaceViewRenderer.setLayoutParams(new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT));
mLayout.addView(view);//将SurfaceView添加到自定义宽高为百分比的布局控件中
}
/**
* 该视频对象是否全屏显示
*
* @return true false
*/
public Boolean isFullScreen() {
return w == 100 || h == 100;
}
/**
* 是否点击了该视频对象
*
* @param px
* @param py
* @return
*/
public Boolean Hited(int px, int py) {
if (!isFullScreen()) {
int left = x * mScreenWidth / 100;
int top = y * mScreenHeight / 100;
int right = (x + w) * mScreenWidth / 100;
int bottom = (y + h) * mScreenHeight / 100;
if ((px >= left && px <= right) && (py >= top && py <= bottom)) {
return true;
}
}
return false;
}
public void close() {
mLayout.removeView(surfaceViewRenderer);
surfaceViewRenderer.release();
surfaceViewRenderer = null;
videoRenderer = null;
}
}
/**
* 仅用于1大几小
* 1个大像和几个小像的时候设置
* @param is169 比例是否是16:9 true 16:9 false 4:3
* @param direction 显示位置 左边 中间 右边
* @param orientation 排列方式 垂直 横向
*/
public void setVideoViewLayout(boolean is169, int direction, int orientation) {
this.is169 = is169;
this.direction = direction;
this.orientation = orientation;
if (!isSameSize) {
changeSizeWhenRotate(false);
}
}
/**
* 仅用于1大几小
* 旋转屏幕时改变尺寸
* isFirst 是否是第一次 是的话 是不需要更新视频View的
*/
public void changeSizeWhenRotate(boolean isFirst) {
if (is169) {
if (mContext.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {//横屏
SUB_WIDTH = (int) (((mScreenWidth / 5f) * 1.777777f) / (mScreenHeight / 100f));
SUB_HEIGHT=(int) ((mScreenWidth / 5f) / (mScreenWidth / 100f));
} else {
SUB_HEIGHT = (int) (((mScreenWidth / 5f) * 1.777777f) / (mScreenHeight / 100f));
SUB_WIDTH = (int) ((mScreenWidth / 5f) / (mScreenWidth / 100f));
}
} else {
if (mContext.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {//横屏
SUB_WIDTH = (int) (((mScreenWidth / 4f) * 1.33333f) / (mScreenHeight / 100f));
SUB_HEIGHT=(int) ((mScreenWidth / 4f) / (mScreenWidth / 100f));
} else {
SUB_HEIGHT = (int) (((mScreenWidth / 3f) /1.33333f) / (mScreenHeight / 100f));
SUB_WIDTH = (int) ((mScreenWidth / 3f) / (mScreenWidth / 100f));
}
}
if (!isFirst){
updateVideoView1Big();
}
}
/**
* 获取视频窗口的个数
*
* @return
*/
public int getVideoRenderSize() {
int size = mRemoteRenderList.size();
if (LocalVideoRender != null) {
size += 1;
}
return size;
}
/**
* 打开本地摄像头渲染对象
*
* @return
*/
public VideoRenderer openLocalVideoRender() {
int size = getVideoRenderSize();
if (size == 0) {
LocalVideoRender = new VideoView("localRender", rlVideoGroup.getContext(), eglBase, 0, 0, 0, 100, 100);
LocalVideoRender.surfaceViewRenderer.setZOrderMediaOverlay(false);
} else {
LocalVideoRender = new VideoView("localRender", rlVideoGroup.getContext(), eglBase, size, 0, 0, 100, 100);
LocalVideoRender.surfaceViewRenderer.setZOrderMediaOverlay(false);
}
rlVideoGroup.addView(LocalVideoRender.mLayout, -1);
LocalVideoRender.mLayout.setPosition(
LocalVideoRender.x, LocalVideoRender.y, LocalVideoRender.w, LocalVideoRender.h);
LocalVideoRender.surfaceViewRenderer.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FILL);
LocalVideoRender.flLoading.setVisibility(VISIBLE);
LocalVideoRender.surfaceViewRenderer.addFrameListener(new EglRenderer.FrameListener() {
@Override
public void onFrame(Bitmap frame) {
Log.d("surfaceView", frame.toString());
LocalVideoRender.surfaceViewRenderer.post(new Runnable() {
@Override
public void run() {
LocalVideoRender.flLoading.setVisibility(View.GONE);
}
});
}
}, 1f);
if (isSameSize){
updateVideoViewSameSize();
}else {
updateVideoView1Big();
}
LocalVideoRender.videoRenderer = new VideoRenderer(LocalVideoRender.surfaceViewRenderer);
return LocalVideoRender.videoRenderer;
}
/**
* 移除本地视频渲染对象
*/
public void removeLocalVideoRender() {
if (LocalVideoRender != null) {
LocalVideoRender.close();
LocalVideoRender.videoRenderer = null;
rlVideoGroup.removeView(LocalVideoRender.mLayout);
LocalVideoRender = null;
if (isSameSize){
updateVideoViewSameSize();
}else {
updateVideoView1Big();
}
}
}
/**
* 打开远程视频渲染对象
*
* @param videoId 视频ID
* @return
*/
public VideoRenderer openRemoteVideoRender(final String videoId) {
VideoView remoteVideoRender = mRemoteRenderList.get(videoId);
if (remoteVideoRender == null) {
int size = getVideoRenderSize();
if (size == 0) {
remoteVideoRender = new VideoView(videoId, rlVideoGroup.getContext(), eglBase, 0, 0, 0, 100, 100);
} else {
remoteVideoRender = new VideoView(videoId, rlVideoGroup.getContext(), eglBase, size, 0, 0, 0, 0);
remoteVideoRender.surfaceViewRenderer.setZOrderMediaOverlay(true);
}
rlVideoGroup.addView(remoteVideoRender.mLayout, -1);
remoteVideoRender.mLayout.setPosition(
remoteVideoRender.x, remoteVideoRender.y, remoteVideoRender.w, remoteVideoRender.h);
remoteVideoRender.surfaceViewRenderer.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FILL);
remoteVideoRender.flLoading.setVisibility(VISIBLE);
final VideoView finalRemoteVideoRender = remoteVideoRender;
remoteVideoRender.surfaceViewRenderer.addFrameListener(new EglRenderer.FrameListener() {
@Override
public void onFrame(Bitmap frame) {
finalRemoteVideoRender.surfaceViewRenderer.post(new Runnable() {
@Override
public void run() {
finalRemoteVideoRender.flLoading.setVisibility(View.GONE);
}
});
}
}, 1f);
remoteVideoRender.videoRenderer = new VideoRenderer(remoteVideoRender.surfaceViewRenderer);
mRemoteRenderList.put(videoId, remoteVideoRender);
if (isSameSize){
updateVideoViewSameSize();
}else {
updateVideoView1Big();
}
if (isHost || (!isHost && videoId.equals("LocalCameraRender"))) {
remoteVideoRender.btnHangUp.setVisibility(View.VISIBLE);
remoteVideoRender.btnHangUp.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (null != videoLayoutOnclickEvent) {
videoLayoutOnclickEvent.onCloseVideoRender(v, videoId);
}
}
});
}
}
return remoteVideoRender.videoRenderer;
}
/**
* 移除远程像
*
* @param videoId
*/
public void removeRemoteRender(String videoId) {
VideoView remoteVideoRender = mRemoteRenderList.get(videoId);
if (remoteVideoRender != null) {
remoteVideoRender.close();
rlVideoGroup.removeView(remoteVideoRender.mLayout);
mRemoteRenderList.remove(videoId);
sortVideoRenderIndex();
if (isSameSize){
updateVideoViewSameSize();
}else {
updateVideoView1Big();
}
}
}
public void sortVideoRenderIndex() {
List> list = new ArrayList>(mRemoteRenderList.entrySet());
for (int i = 0; i < list.size(); i++) {
list.get(i).getValue().index = i + 1;
}
}
//第一种 1个大 多个小 小像从中间位置开始 最多5个
/**
* 1个大像 5个小像示例
* 小像横排/竖排排列
* 小像从左边 中间 右边开始排列
*/
private void updateVideoView1Big() {
int size = mRemoteRenderList.size();
if (size == 0) {
if (LocalVideoRender != null) {
LocalVideoRender.x = 0;
LocalVideoRender.y = 0;
LocalVideoRender.w = 100;
LocalVideoRender.h = 100;
LocalVideoRender.mLayout.setPosition(0, 0, 100, 100);
LocalVideoRender.surfaceViewRenderer.requestLayout();
}
} else {
int startX = 0;
int startY = 100-SUB_HEIGHT-2;
if (orientation== LinearLayout.HORIZONTAL){
if (direction == Gravity.CENTER) {
startX = (100 - (SUB_WIDTH * size)) / 2;//小像起始位置
} else if (direction == Gravity.LEFT) {
startX = 0;
} else if (direction == Gravity.RIGHT) {
startX = 100 - SUB_WIDTH;
} else {
startX = (100 - (SUB_WIDTH * size)) / 2;
}
}else {
if (direction == Gravity.CENTER) {
startX = (100 - SUB_WIDTH) / 2;//小像起始位置
} else if (direction == Gravity.LEFT) {
startX = 0;
} else if (direction == Gravity.RIGHT) {
startX = 100 - SUB_WIDTH-5;
} else {
startX = (100 - SUB_WIDTH) / 2;
}
}
if (LocalVideoRender != null) {
LocalVideoRender.x = 0;
LocalVideoRender.y = 0;
LocalVideoRender.w = 100;
LocalVideoRender.h = 100;
LocalVideoRender.mLayout.setPosition(0, 0, 100, 100);
LocalVideoRender.surfaceViewRenderer.requestLayout();
}
Iterator> iter = mRemoteRenderList.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry entry = iter.next();
VideoView render = entry.getValue();
if (orientation == LinearLayout.HORIZONTAL) {
if (direction == Gravity.CENTER) {
render.x = startX + (render.index - 1) * SUB_WIDTH;
} else if (direction == Gravity.LEFT) {
render.x = startX + (render.index - 1) * SUB_WIDTH;
} else if (direction == Gravity.RIGHT) {
render.x = startX - (render.index - 1) * SUB_WIDTH;
} else {
render.x = startX + (render.index - 1) * SUB_WIDTH;
}
render.y = startY;
} else {
render.x=startX;
render.y = startY - (render.index - 1) * SUB_HEIGHT;
}
render.w = SUB_WIDTH;
render.h = SUB_HEIGHT;
render.mLayout.setPosition(render.x, render.y, render.w, render.h);
render.surfaceViewRenderer.requestLayout();
}
}
}
/**
* 适合横屏
* 平均大小模式示例
* 1个全屏 2个上下或左右个1 3个品字形状 4个田字形状 5个上2下3 6个上3下3
*/
public void updateVideoViewSameSize() {
int HEIGHT, WIDTH;
//平均大小模式
int size = mRemoteRenderList.size();
if (size == 0) {
LocalVideoRender.mLayout.setPosition(0, 0, 100, 100);
LocalVideoRender.surfaceViewRenderer.requestLayout();
} else if (size == 1) {
if (!is169) {
HEIGHT = (int) (((mScreenWidth / 2f) / 1.33333f) / (mScreenHeight / 100));
WIDTH = (int) ((mScreenWidth / 2f) / (mScreenWidth / 100));
} else {
HEIGHT = (int) (((mScreenWidth / 2f) / 1.77777f) / (mScreenHeight / 100));
WIDTH = (int) ((mScreenWidth / 2f) / (mScreenWidth / 100));
}
int Y = (100 - HEIGHT) / 2;
Iterator> iter = mRemoteRenderList.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry entry = iter.next();
VideoView render = entry.getValue();
LocalVideoRender.mLayout.setPosition(0, Y, WIDTH, HEIGHT);
LocalVideoRender.surfaceViewRenderer.requestLayout();
if (render.index == 1) {
render.mLayout.setPosition(WIDTH, Y, WIDTH, HEIGHT);
render.surfaceViewRenderer.requestLayout();
}
}
} else if (size == 2) {
if (!is169) {
WIDTH = (int) (((mScreenHeight / 2f) * 1.33333f) / (mScreenWidth / 100));
HEIGHT = (int) ((mScreenHeight / 2f) / (mScreenHeight / 100));
} else {
WIDTH = (int) (((mScreenHeight / 2f) * 1.77777f) / (mScreenWidth / 100));
HEIGHT = (int) ((mScreenHeight / 2f) / (mScreenHeight / 100));
}
int X = 0;
int Y = 0;
// int WIDTH = 100 / 2;
// int HEIGHT = 50;
Iterator> iter = mRemoteRenderList.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry entry = iter.next();
VideoView render = entry.getValue();
LocalVideoRender.mLayout.setPosition((100 - WIDTH) / 2, Y, WIDTH, HEIGHT);
LocalVideoRender.surfaceViewRenderer.requestLayout();
if (render.index == 1) {
render.mLayout.setPosition((100 - 2 * WIDTH) / 2, Y + HEIGHT, WIDTH, HEIGHT);
render.surfaceViewRenderer.requestLayout();
} else if (render.index == 2) {
render.mLayout.setPosition((100 - 2 * WIDTH) / 2 + WIDTH, Y + HEIGHT, WIDTH, HEIGHT);
render.surfaceViewRenderer.requestLayout();
}
}
} else if (size == 3) {
if (!is169) {
WIDTH = (int) (((mScreenHeight / 2f) * 1.33333f) / (mScreenWidth / 100));
HEIGHT = (int) ((mScreenHeight / 2f) / (mScreenHeight / 100));
} else {
WIDTH = (int) (((mScreenHeight / 2f) * 1.77777f) / (mScreenWidth / 100));
HEIGHT = (int) ((mScreenHeight / 2f) / (mScreenHeight / 100));
}
int X = 0;
int Y = 0;
// int WIDTH = 50;
// int HEIGHT = 50;
Iterator> iter = mRemoteRenderList.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry entry = iter.next();
VideoView render = entry.getValue();
LocalVideoRender.mLayout.setPosition((100 - WIDTH * 2) / 2, Y, WIDTH, HEIGHT);
LocalVideoRender.surfaceViewRenderer.requestLayout();
if (render.index == 1) {
render.mLayout.setPosition((100 - 2 * WIDTH) / 2 + WIDTH, Y, WIDTH, HEIGHT);
render.surfaceViewRenderer.requestLayout();
} else if (render.index == 2) {
render.mLayout.setPosition((100 - 2 * WIDTH) / 2, Y + HEIGHT, WIDTH, HEIGHT);
render.surfaceViewRenderer.requestLayout();
} else if (render.index == 3) {
render.mLayout.setPosition((100 - 2 * WIDTH) / 2 + WIDTH, Y + HEIGHT, WIDTH, HEIGHT);
render.surfaceViewRenderer.requestLayout();
}
}
} else if (size == 4) {
if (!is169) {
WIDTH = (int) (((mScreenHeight / 2f) * 1.33333f) / (mScreenWidth / 100));
HEIGHT = (int) ((mScreenHeight / 2f) / (mScreenHeight / 100));
} else {
HEIGHT = (int) (((mScreenWidth / 3f) / 1.77777f) / (mScreenHeight / 100));
WIDTH = (int) ((mScreenWidth / 3f) / (mScreenWidth / 100));
}
int X = (100 - WIDTH * 3) / 2;
int Y = (100-HEIGHT*2)/2;
Iterator> iter = mRemoteRenderList.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry entry = iter.next();
VideoView render = entry.getValue();
LocalVideoRender.mLayout.setPosition((100 - WIDTH * 2) / 2, Y, WIDTH, HEIGHT);
LocalVideoRender.surfaceViewRenderer.requestLayout();
if (render.index == 1) {
render.mLayout.setPosition((100 - WIDTH * 2) / 2 + WIDTH, Y, WIDTH, HEIGHT);
render.surfaceViewRenderer.requestLayout();
} else {
if (render.index % 3 == 0) {
render.mLayout.setPosition(X, Y + HEIGHT, WIDTH, HEIGHT);
render.surfaceViewRenderer.requestLayout();
} else {
render.mLayout.setPosition(X + (render.index % 3 * WIDTH), Y + HEIGHT, WIDTH, HEIGHT);
render.surfaceViewRenderer.requestLayout();
}
}
}
} else {
if (!is169) {
WIDTH = (int) (((mScreenHeight / 2f) * 1.33333f) / (mScreenWidth / 100));
HEIGHT = (int) ((mScreenHeight / 2f) / (mScreenHeight / 100));
} else {
HEIGHT = (int) (((mScreenWidth / 3f) / 1.77777f) / (mScreenHeight / 100));
WIDTH = (int) ((mScreenWidth / 3f) / (mScreenWidth / 100));
}
int X = (100 - WIDTH * 3) / 2;
int Y = (100-HEIGHT*2)/2;
// int WIDTH = 100 / 3;
// int HEIGHT = 30;
Iterator> iter = mRemoteRenderList.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry entry = iter.next();
VideoView render = entry.getValue();
LocalVideoRender.mLayout.setPosition(X, Y, WIDTH, HEIGHT);
LocalVideoRender.surfaceViewRenderer.requestLayout();
if (render.index == 1) {
render.mLayout.setPosition(X + WIDTH, Y, WIDTH, HEIGHT);
render.surfaceViewRenderer.requestLayout();
} else if (render.index == 2) {
render.mLayout.setPosition(X + (WIDTH * 2), Y, WIDTH, HEIGHT);
render.surfaceViewRenderer.requestLayout();
} else if (render.index == 3) {
render.mLayout.setPosition(X, HEIGHT+Y, WIDTH, HEIGHT);
render.surfaceViewRenderer.requestLayout();
} else if (render.index == 4) {
render.mLayout.setPosition(X + WIDTH, HEIGHT+Y, WIDTH, HEIGHT);
render.surfaceViewRenderer.requestLayout();
} else if (render.index == 5) {
render.mLayout.setPosition(X + (WIDTH * 2), HEIGHT+Y, WIDTH, HEIGHT);
render.surfaceViewRenderer.requestLayout();
}
}
}
}
private void SwitchViewToFullscreen(VideoView view1, VideoView fullscrnView) {
if (view1==null||fullscrnView==null){
return;
}
int index, x, y, w, h;
index = view1.index;
x = view1.x;
y = view1.y;
w = view1.w;
h = view1.h;
view1.index = fullscrnView.index;
view1.x = fullscrnView.x;
view1.y = fullscrnView.y;
view1.w = fullscrnView.w;
view1.h = fullscrnView.h;
fullscrnView.index = index;
fullscrnView.x = x;
fullscrnView.y = y;
fullscrnView.w = w;
fullscrnView.h = h;
fullscrnView.mLayout.setPosition(fullscrnView.x, fullscrnView.y, fullscrnView.w, fullscrnView.h);
view1.mLayout.setPosition(view1.x, view1.y, view1.w, view1.h);
updateVideoLayout(view1, fullscrnView);
}
/**
* 视频切换后更新视频的布局
*
* @param view1
* @param view2
*/
private void updateVideoLayout(VideoView view1, VideoView view2) {
if (view1.isFullScreen()) {
view1.surfaceViewRenderer.setZOrderMediaOverlay(false);
view2.surfaceViewRenderer.setZOrderMediaOverlay(true);
view1.mLayout.requestLayout();
view2.mLayout.requestLayout();
rlVideoGroup.removeView(view1.mLayout);
rlVideoGroup.removeView(view2.mLayout);
rlVideoGroup.addView(view1.mLayout, -1);
rlVideoGroup.addView(view2.mLayout, 0);
} else if (view2.isFullScreen()) {
view1.surfaceViewRenderer.setZOrderMediaOverlay(true);
view2.surfaceViewRenderer.setZOrderMediaOverlay(false);
view2.mLayout.requestLayout();
view1.mLayout.requestLayout();
rlVideoGroup.removeView(view1.mLayout);
rlVideoGroup.removeView(view2.mLayout);
rlVideoGroup.addView(view1.mLayout, 0);
rlVideoGroup.addView(view2.mLayout, -1);
} else {
view1.mLayout.requestLayout();
view2.mLayout.requestLayout();
rlVideoGroup.removeView(view1.mLayout);
rlVideoGroup.removeView(view2.mLayout);
rlVideoGroup.addView(view1.mLayout, 0);
rlVideoGroup.addView(view2.mLayout, 0);
}
}
/**
* 获取全屏的界面
*
* @return
*/
private VideoView GetFullScreen() {
if (LocalVideoRender.isFullScreen()) {
return LocalVideoRender;
}
Iterator> iter = mRemoteRenderList.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry entry = iter.next();
String peerId = entry.getKey();
VideoView render = entry.getValue();
if (render.isFullScreen())
return render;
}
return null;
}
}
================================================
FILE: app/src/main/java/org/ar/widgets/AppBaseDialogFragment.java
================================================
package org.ar.widgets;
import android.os.Bundle;
import android.support.annotation.LayoutRes;
import android.support.annotation.Nullable;
import android.support.v4.app.DialogFragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
/**
* Created by KathLine on 2016/12/30.
*/
public abstract class AppBaseDialogFragment extends DialogFragment {
private View view;
public AppBaseDialogFragment() {
// Required empty public constructor
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE);//必须放在setContextView之前调用, 去掉Dialog中的蓝线
// view = LayoutInflater.from(getActivity()).inflate(getContentViewID(), container, false);
view = inflater.inflate(getContentViewID(), container);
setLayout();
initData(view);
return view;
}
protected void setLayout() {
}
@LayoutRes
protected abstract int getContentViewID();
protected abstract void initData(View view);
}
================================================
FILE: app/src/main/java/org/ar/widgets/BaseDialog.java
================================================
package org.ar.widgets;
import android.app.Dialog;
import android.content.Context;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
/**
* Created by KathLine on 2016/11/8.
*/
public abstract class BaseDialog extends Dialog {
public BaseDialog(Context context) {
super(context);
}
protected abstract void onTouchOutside();
@Override
public boolean onTouchEvent(MotionEvent event) {
/* 触摸外部弹窗 */
if (isOutOfBounds(getContext(), event)) {
onTouchOutside();
}
return super.onTouchEvent(event);
}
/**
* 判断当前用户触摸是否超出了Dialog的显示区域
*
* @param context
* @param event
* @return
*/
private boolean isOutOfBounds(Context context, MotionEvent event) {
final int x = (int) event.getX();
final int y = (int) event.getY();
final int slop = ViewConfiguration.get(context).getScaledWindowTouchSlop();
final View decorView = getWindow().getDecorView();
return (x < -slop) || (y < -slop) || (x > (decorView.getWidth() + slop))
|| (y > (decorView.getHeight() + slop));
}
}
================================================
FILE: app/src/main/java/org/ar/widgets/CustomDialog.java
================================================
package org.ar.widgets;
import android.content.Context;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.support.annotation.LayoutRes;
import android.view.Gravity;
import android.view.Window;
import android.view.WindowManager;
import org.ar.rtmpc.R;
/**
* Created by KathLine on 2016/8/2.
*/
public class CustomDialog extends BaseDialog {
protected Builder builder;
protected int layoutId;
protected int gravity;
protected int animId;
protected boolean backgroundDrawableable;
protected float dimAmount;
protected boolean cancelable;
protected boolean existDialogLined;
public boolean isFullScreen;
protected int width;
protected int height;
protected CustomDialog(Context context, Builder builder) {
super(context);
this.builder = builder;
layoutId = builder.layoutId;
gravity = builder.gravity;
animId = builder.animId;
backgroundDrawableable = builder.backgroundDrawableable;
dimAmount = builder.dimAmount;
cancelable = builder.cancelable;
existDialogLined = builder.existDialogLined;
isFullScreen = builder.isFullScreen;
width = builder.width;
height = builder.height;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (existDialogLined) {
requestWindowFeature(Window.FEATURE_NO_TITLE);
}
setContentView(layoutId);
Window window = getWindow();
window.setGravity(gravity);
window.setWindowAnimations(animId);
window.getDecorView().setPadding(0, 0, 0, 0);
WindowManager.LayoutParams lp = window.getAttributes();
if (width != 0 && height != 0) {
lp.width = width;
lp.height = height;
}
if (isFullScreen) {
window.setFlags(
WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);//设置全屏,即没有系统状态栏
}
if (backgroundDrawableable) {
window.setBackgroundDrawable(new ColorDrawable(0));
}
if (dimAmount < 0f || dimAmount > 1f) {
throw new RuntimeException("透明度必须在0~1之间");
}else {
lp.dimAmount = dimAmount;
}
window.setAttributes(lp);
setCancelable(cancelable);
if (cancelable) {
setCanceledOnTouchOutside(true);
}
}
@Override
protected void onTouchOutside() {
if (onTouchOutsideListener != null){
onTouchOutsideListener.touchOutSide();
}
}
public interface onTouchOutsideListener{
void touchOutSide();
}
public onTouchOutsideListener onTouchOutsideListener;
public void setOnTouchOutsideListener(onTouchOutsideListener listener){
onTouchOutsideListener = listener;
}
public Builder getBuilder() {
return builder;
}
public CustomDialog show(Builder.onInitListener listener) {
this.show();
if (listener != null) {
listener.init(this);
}
return this;
}
public static class Builder {
public Context context;
public int layoutId;
public int gravity;
public int animId;
public boolean backgroundDrawableable;
public float dimAmount;
public boolean cancelable;
public boolean existDialogLined;
public boolean isFullScreen;
public int width;
public int height;
public Builder(Context context) {
this.context = context;
layoutId = R.layout.dialog_base;
gravity = Gravity.CENTER;
animId = R.style.default_dialog_style;
backgroundDrawableable = false;
dimAmount = 0.5f;
cancelable = true;
existDialogLined = true;
width = 0;
height = 0;
}
public Builder setContentView(@LayoutRes int layoutId) {
this.layoutId = layoutId;
return this;
}
/**
* 必须使用Gravity的静态常量,默认在中间弹出
*
* @param gravity 详见{@link Gravity}
* @return
* @see Gravity
*/
public Builder setGravity(int gravity) {
this.gravity = gravity;
return this;
}
/**
* 设置Dialog弹出和Dialog退出的动画
*
* @param animId
* @return
*/
public Builder setAnimId(int animId) {
this.animId = animId;
return this;
}
/**
* Creates a new set of layout parameters with the specified width
* and height.
*
* @param width the width, either set WindowManager.LayoutParams.WRAP_CONTENT or
* WindowManager.LayoutParams.FILL_PARENT (replaced by WindowManager.LayoutParams.MATCH_PARENT in
* API Level 8), or a fixed size in pixels
* @param height the height, either set WindowManager.LayoutParams.WRAP_CONTENT or
* WindowManager.LayoutParams.FILL_PARENT (replaced by WindowManager.LayoutParams.MATCH_PARENT in
* API Level 8), or a fixed size in pixels
* @return
*/
public Builder setLayoutParams(int width, int height) {
this.width = width;
this.height = height;
return this;
}
/**
* 是否给Dialog的背景设置透明,默认false
*
* @param backgroundDrawableable
* @return
*/
public Builder setBackgroundDrawable(boolean backgroundDrawableable) {
this.backgroundDrawableable = backgroundDrawableable;
return this;
}
/**
* 设置Dialog之外的背景透明度,0~1之间,默认值 0.5f,半透明
*
* @param dimAmount
* @return
*/
public Builder setDimAmount(float dimAmount) {
this.dimAmount = dimAmount;
return this;
}
/**
* 设置Dialog是否可以关闭在Dialog之外的区域,默认true
*
* @param cancelable
* @return
*/
public Builder setCancelable(boolean cancelable) {
this.cancelable = cancelable;
return this;
}
/**
* 如果存在Holo主题下Dialog有蓝色线(含有标题栏)可以尝试调用该方法,默认不存在
*
* @param existDialogLined
* @return
*/
public Builder setExistDialogLined(boolean existDialogLined) {
this.existDialogLined = existDialogLined;
return this;
}
/**
* 是否设置全屏模式,指的是去除系统状态栏,默认不去除
*
* @param isFullScreen
* @return
*/
public Builder setFullScreen(boolean isFullScreen) {
this.isFullScreen = isFullScreen;
return this;
}
public interface onInitListener {
/**
* 绑定控件
*
* @param customDialog
*/
void init(CustomDialog customDialog);
}
public CustomDialog build() {
return new CustomDialog(context, this);
}
/**
* 如果对话框仅仅起提示作用,可以传入null
*
* @param listener
* @return
*/
public CustomDialog show(onInitListener listener) {
CustomDialog dialog = build();
dialog.show();
if (listener != null) {
listener.init(dialog);
}
return dialog;
}
}
}
================================================
FILE: app/src/main/java/org/ar/widgets/KeyboardDialogFragment.java
================================================
package org.ar.widgets;
import android.app.Activity;
import android.content.Context;
import android.content.DialogInterface;
import android.content.res.Configuration;
import android.graphics.drawable.ColorDrawable;
import android.support.v4.app.DialogFragment;
import android.text.TextUtils;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import org.ar.rtmpc.R;
import java.util.Timer;
import java.util.TimerTask;
/**
* Created by KathLine on 2016/12/30.
*/
public class KeyboardDialogFragment extends AppBaseDialogFragment {
TextView close;
EditText editSendMessage;
Button btnSend;
private static InputMethodManager imm;
public KeyboardDialogFragment() {
// Required empty public constructor
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
}
public interface EdittextListener {
void setTextStr(String text);
void dismiss(DialogFragment dialogFragment);
}
private EdittextListener edittextListener;
public void setEdittextListener(EdittextListener listener) {
edittextListener = listener;
}
@Override
protected void setLayout() {
Window window = getDialog().getWindow();
window.getDecorView().setPadding(0, 0, 0, 0);
window.setBackgroundDrawable(new ColorDrawable(0));//背景透明
WindowManager.LayoutParams lp = window.getAttributes();
lp.width = ViewGroup.LayoutParams.MATCH_PARENT;
lp.height =ViewGroup.LayoutParams.MATCH_PARENT;
lp.dimAmount = 0;
window.setAttributes(lp);
}
@Override
protected int getContentViewID() {
return R.layout.dialogfragment_keyboard;
}
@Override
protected void initData(View view) {
close=view.findViewById(R.id.close);
close.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (edittextListener != null) {
hideKeyboard();
dismiss();
}
}
});
editSendMessage=view.findViewById(R.id.edit_send_message);
btnSend=view.findViewById(R.id.btn_send);
btnSend.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
String text = editSendMessage.getText().toString();
if (!TextUtils.isEmpty(text)) {
if (edittextListener != null) {
edittextListener.setTextStr(text);
editSendMessage.setText("");
hideKeyboard();
dismiss();
}
}
}
});
Timer timer = new Timer();
timer.schedule(new TimerTask() {
public void run() {
showKeyboard(editSendMessage.getContext(), editSendMessage);
}
},
150);
}
private void hideKeyboard() {
try {
InputMethodManager imm = (InputMethodManager) editSendMessage.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm!=null&&getDialog()!=null) {
imm.hideSoftInputFromWindow(getDialog().getCurrentFocus().getWindowToken(),
InputMethodManager.HIDE_NOT_ALWAYS);
}
}catch (Exception e){
}
}
@Override
public void onDismiss(DialogInterface dialog) {
super.onDismiss(dialog);
if (edittextListener != null) {
edittextListener.dismiss(this);
}
}
public void hideKeyboard(Context context, View view) {
try {
view.requestFocus();
imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm!=null) {
imm.hideSoftInputFromWindow(view.getApplicationWindowToken(),
InputMethodManager.HIDE_NOT_ALWAYS);
}
}catch (Exception e){
}
}
public void hideKeyboard(Activity activity) {
try {
imm = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm!=null) {
imm.hideSoftInputFromWindow(activity.getCurrentFocus().getWindowToken(),
InputMethodManager.HIDE_NOT_ALWAYS);
}
}catch (Exception e){
}
}
public void showKeyboard(Context context, View view) {
try {
imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
// imm.showSoftInput(view, 0);
if (imm!=null) {
imm.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT);
}
}catch (Exception e){
}
}
}
================================================
FILE: app/src/main/res/anim/push_bottom_in.xml
================================================
================================================
FILE: app/src/main/res/anim/push_bottom_out.xml
================================================
================================================
FILE: app/src/main/res/color/select_text_host_input.xml
================================================
================================================
FILE: app/src/main/res/drawable/bg_host_head.xml
================================================
================================================
FILE: app/src/main/res/drawable/bg_list_item.xml
================================================
-
-
================================================
FILE: app/src/main/res/drawable/bg_white.xml
================================================
-
-
================================================
FILE: app/src/main/res/drawable/default_input_bg.xml
================================================
-
-
-
-
-
-
================================================
FILE: app/src/main/res/drawable/line_anim.xml
================================================
================================================
FILE: app/src/main/res/drawable/list_item_bg.xml
================================================
-
================================================
FILE: app/src/main/res/drawable/selector_apply.xml
================================================
================================================
FILE: app/src/main/res/drawable/shape_creat_btn_bg.xml
================================================
================================================
FILE: app/src/main/res/drawable/shape_edittext_bg.xml
================================================
================================================
FILE: app/src/main/res/drawable/shape_home_green_btn.xml
================================================
================================================
FILE: app/src/main/res/drawable/shape_meet_id.xml
================================================
================================================
FILE: app/src/main/res/drawable/shape_message.xml
================================================
================================================
FILE: app/src/main/res/drawable/shape_popuwindow.xml
================================================
================================================
FILE: app/src/main/res/drawable/shape_room_apply_audio_line.xml
================================================
================================================
FILE: app/src/main/res/drawable/shape_room_apply_line.xml
================================================
================================================
FILE: app/src/main/res/drawable/shape_room_hang_up_line.xml
================================================
================================================
FILE: app/src/main/res/drawable/shape_room_member.xml
================================================
================================================
FILE: app/src/main/res/drawable/shape_room_message.xml
================================================
================================================
FILE: app/src/main/res/drawable/shape_room_name.xml
================================================
================================================
FILE: app/src/main/res/drawable-xxhdpi/selector_video_manager.xml
================================================
-
-
================================================
FILE: app/src/main/res/drawable-xxhdpi/shape_back_btn.xml
================================================
================================================
FILE: app/src/main/res/drawable-xxhdpi/shape_index_button.xml
================================================
================================================
FILE: app/src/main/res/drawable-xxhdpi/shape_index_solid_button.xml
================================================
================================================
FILE: app/src/main/res/layout/activity_audio_guest.xml
================================================
================================================
FILE: app/src/main/res/layout/activity_audio_hoster.xml
================================================
================================================
FILE: app/src/main/res/layout/activity_guest.xml
================================================
================================================
FILE: app/src/main/res/layout/activity_hoster.xml
================================================
================================================
FILE: app/src/main/res/layout/activity_live_list.xml
================================================
================================================
FILE: app/src/main/res/layout/dialog_base.xml
================================================
================================================
FILE: app/src/main/res/layout/dialogfragment_keyboard.xml
================================================
================================================
FILE: app/src/main/res/layout/empty_act_data.xml
================================================
================================================
FILE: app/src/main/res/layout/empty_no_line_data.xml
================================================
================================================
FILE: app/src/main/res/layout/fragment_line.xml
================================================
================================================
FILE: app/src/main/res/layout/item_audio_line.xml
================================================
================================================
FILE: app/src/main/res/layout/item_line.xml
================================================
================================================
FILE: app/src/main/res/layout/item_line_list.xml
================================================
================================================
FILE: app/src/main/res/layout/item_live.xml
================================================
================================================
FILE: app/src/main/res/layout/item_live_chat.xml
================================================
================================================
FILE: app/src/main/res/layout/item_member.xml
================================================
================================================
FILE: app/src/main/res/layout/item_more_futures.xml
================================================
================================================
FILE: app/src/main/res/layout/layout_arvideo.xml
================================================
================================================
FILE: app/src/main/res/values/attrs.xml
================================================
================================================
FILE: app/src/main/res/values/colors.xml
================================================
@color/main_red
@color/main_red
@color/main_red
#0DFFFFFF
#1AFFFFFF
#26FFFFFF
#33FFFFFF
#40FFFFFF
#4DFFFFFF
#59FFFFFF
#66FFFFFF
#73FFFFFF
#80FFFFFF
#8CFFFFFF
#99FFFFFF
#A6FFFFFF
#B3FFFFFF
#BFFFFFFF
#CCFFFFFF
#D9FFFFFF
#E6FFFFFF
#F2FFFFFF
#0D000000
#1A000000
#26000000
#33000000
#40000000
#4D000000
#59000000
#66000000
#73000000
#80000000
#8C000000
#99000000
#A6000000
#B3000000
#BF000000
#CC000000
#D9000000
#E6000000
#F2000000
#50FFFFFF
#999999
#dddddd
#EBEBEB
#FFAC00
#ffbc00
#ffae00
#f5c490
#ffac00
#ffac00
#ff9b00
#7f000000
#333333
#999999
#1F91EF
#999999
#fb4d3d
#cccccc
#ffffff
#88000000
#576b93
#333333
#0986ed
#f3f4f5
#D4333C
#555555
#b38989
#ff9696
#b5b9bf
#3C3C3C
#DBDBDB
#A67838
#FF4A54
#CFCFCF
#EDEDED
#333333
#007aff
#E8E8E8
#dddddd
#8C8C8C
#FCFCFC
#66000000
#ffffff
#FFFF00
#FFFFFF
#000000
#808080
#FF0000
#dddddd
#F0F8FF
#8db4eb
#FAEBD7
================================================
FILE: app/src/main/res/values/dimens.xml
================================================
16dp
16dp
10sp
12sp
14sp
16sp
18sp
22sp
5dp
5dp
15dp
15dp
5dp
50dp
40dp
30dpen>
75dp
12dp
5dp
0dp
36dp
32dp
14sp
56dp
4dp
0dp
12dp
48dp
12dp
250dp
8dp
25.0dip
250.0dip
================================================
FILE: app/src/main/res/values/ids.xml
================================================
================================================
FILE: app/src/main/res/values/strings.xml
================================================
AR直播连麦
AR Live Hybird
多人连麦直播
www.anyrtc.io Version: ${VERSION}
直播主题
发起直播
发起视频直播
发起视频直播音频连麦
发起音频直播
音频直播
请创建一个直播主题
开始直播
RTMP 连接成功
RTMP 连接失败
RTC 连接成功
RTC 连接失败
重连服务器:%1$d 次
RTMP 延迟:%1$s 网络:%2$s
在线观看人数:%1$d
主播已离开,2秒后关闭页面
主播还未开启直播, 2秒后关闭页面
设置
连麦
音频连麦
挂断
%1$s 请求连线
同意
拒绝
连线已断开
主播拒绝连线
连线已达最大人数
请去AnyRTC官网申请账号,如有疑问请联系客服!
退出
确定
取消
您正在进行直播,确定要退出吗?
您正在与主播进行连麦互动,确定要退出吗?
缺少录音权限
缺少摄像头权限
正常
未知错误
SDK调用异常
网络错误
直播出错
服务不支持的错误请求
认证失败
此开发者信息不存在
服务器内部数据库错误
账号欠费
账号被锁定
强制离开
连麦互动视频直播正在进行,快来围观...
弹幕
发送
来了,欢迎~
关闭
切换
分享至微信
复制hls成功
超高清
高清
标清
流畅
橫屏直播
竖屏直播
================================================
FILE: app/src/main/res/values/styles.xml
================================================
================================================
FILE: app/src/main/res/values-w820dp/dimens.xml
================================================
64dp
================================================
FILE: app/src/test/java/org/ar/rtmpc/ExampleUnitTest.java
================================================
package org.ar.rtmpc;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* To work on unit tests, switch the Test Artifact in the Build Variants view.
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() throws Exception {
assertEquals(4, 2 + 2);
}
}
================================================
FILE: build.gradle
================================================
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.3'
}
}
allprojects {
repositories {
jcenter()
maven { url "https://jitpack.io" }
// maven{
// url 'file://D:\\liuxiaozhong\\LocalMaven'
// }
google()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
================================================
FILE: gradle/wrapper/gradle-wrapper.properties
================================================
#Wed Sep 13 13:52:57 CST 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
================================================
FILE: gradle.properties
================================================
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
# Default value: -Xmx10248m -XX:MaxPermSize=256m
# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
android.injected.testOnly=false
================================================
FILE: gradlew
================================================
#!/usr/bin/env bash
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
================================================
FILE: gradlew.bat
================================================
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windowz variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
================================================
FILE: settings.gradle
================================================
include ':app'