xuxinyi 1 год назад
Сommit
caeeac16cb
100 измененных файлов с 3798 добавлено и 0 удалено
  1. 15 0
      .gitignore
  2. 3 0
      .idea/.gitignore
  3. 6 0
      .idea/compiler.xml
  4. 18 0
      .idea/deploymentTargetSelector.xml
  5. 20 0
      .idea/gradle.xml
  6. 6 0
      .idea/kotlinc.xml
  7. 10 0
      .idea/migrations.xml
  8. 10 0
      .idea/misc.xml
  9. 6 0
      .idea/vcs.xml
  10. 35 0
      README.md
  11. 1 0
      app/.gitignore
  12. 65 0
      app/build.gradle
  13. 22 0
      app/proguard-rules.pro
  14. 26 0
      app/src/androidTest/java/io/anyrtc/liveplayer/ExampleInstrumentedTest.java
  15. 37 0
      app/src/main/AndroidManifest.xml
  16. 27 0
      app/src/main/java/io/anyrtc/liveplayer/BaseActivity.kt
  17. 161 0
      app/src/main/java/io/anyrtc/liveplayer/Ext.kt
  18. 105 0
      app/src/main/java/io/anyrtc/liveplayer/InputActivity.kt
  19. 64 0
      app/src/main/java/io/anyrtc/liveplayer/MainActivity.kt
  20. 186 0
      app/src/main/java/io/anyrtc/liveplayer/PullActivity.kt
  21. 206 0
      app/src/main/java/io/anyrtc/liveplayer/PushActivity.kt
  22. 92 0
      app/src/main/java/io/anyrtc/liveplayer/RecordThread.java
  23. 34 0
      app/src/main/java/io/anyrtc/liveplayer/ScreenUtils.kt
  24. 5 0
      app/src/main/res/color/color_options.xml
  25. BIN
      app/src/main/res/drawable-xxhdpi/img_audio_normol.png
  26. BIN
      app/src/main/res/drawable-xxhdpi/img_audio_select.png
  27. BIN
      app/src/main/res/drawable-xxhdpi/img_beauty.png
  28. BIN
      app/src/main/res/drawable-xxhdpi/img_beauty_normol.png
  29. BIN
      app/src/main/res/drawable-xxhdpi/img_beauty_select.png
  30. BIN
      app/src/main/res/drawable-xxhdpi/img_copy.png
  31. BIN
      app/src/main/res/drawable-xxhdpi/img_dou.png
  32. BIN
      app/src/main/res/drawable-xxhdpi/img_exit.png
  33. BIN
      app/src/main/res/drawable-xxhdpi/img_fullscreen.png
  34. BIN
      app/src/main/res/drawable-xxhdpi/img_live.png
  35. BIN
      app/src/main/res/drawable-xxhdpi/img_mirro_normol.png
  36. BIN
      app/src/main/res/drawable-xxhdpi/img_mirro_select.png
  37. BIN
      app/src/main/res/drawable-xxhdpi/img_pause.png
  38. BIN
      app/src/main/res/drawable-xxhdpi/img_play.png
  39. BIN
      app/src/main/res/drawable-xxhdpi/img_pull.png
  40. BIN
      app/src/main/res/drawable-xxhdpi/img_push.png
  41. BIN
      app/src/main/res/drawable-xxhdpi/img_replay.png
  42. BIN
      app/src/main/res/drawable-xxhdpi/img_small_video.png
  43. BIN
      app/src/main/res/drawable-xxhdpi/img_snap.png
  44. BIN
      app/src/main/res/drawable-xxhdpi/img_switch.png
  45. BIN
      app/src/main/res/drawable-xxhdpi/img_switch_camera.png
  46. BIN
      app/src/main/res/drawable-xxhdpi/img_todo.png
  47. BIN
      app/src/main/res/drawable-xxhdpi/img_tou.png
  48. BIN
      app/src/main/res/drawable-xxhdpi/img_video_normol.png
  49. BIN
      app/src/main/res/drawable-xxhdpi/img_video_select.png
  50. 170 0
      app/src/main/res/drawable/ic_launcher_background.xml
  51. 6 0
      app/src/main/res/drawable/img_back.xml
  52. 9 0
      app/src/main/res/drawable/img_exit.xml
  53. 5 0
      app/src/main/res/drawable/select_audio.xml
  54. 5 0
      app/src/main/res/drawable/select_beauty.xml
  55. 5 0
      app/src/main/res/drawable/select_mirro.xml
  56. 5 0
      app/src/main/res/drawable/select_options.xml
  57. 5 0
      app/src/main/res/drawable/select_video.xml
  58. 5 0
      app/src/main/res/drawable/shape_blue.xml
  59. 5 0
      app/src/main/res/drawable/shape_check.xml
  60. 5 0
      app/src/main/res/drawable/shape_edit.xml
  61. 5 0
      app/src/main/res/drawable/shape_gray.xml
  62. 5 0
      app/src/main/res/drawable/shape_normol.xml
  63. 5 0
      app/src/main/res/drawable/shape_white.xml
  64. 215 0
      app/src/main/res/layout/activity_input.xml
  65. 206 0
      app/src/main/res/layout/activity_main.xml
  66. 110 0
      app/src/main/res/layout/activity_pull.xml
  67. 151 0
      app/src/main/res/layout/activity_push.xml
  68. BIN
      app/src/main/res/mipmap-hdpi/ic_launcher.webp
  69. BIN
      app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
  70. BIN
      app/src/main/res/mipmap-mdpi/ic_launcher.webp
  71. BIN
      app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
  72. BIN
      app/src/main/res/mipmap-xhdpi/ic_launcher.webp
  73. BIN
      app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
  74. BIN
      app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
  75. BIN
      app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
  76. BIN
      app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
  77. BIN
      app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
  78. 16 0
      app/src/main/res/values-night/themes.xml
  79. 8 0
      app/src/main/res/values/array.xml
  80. 10 0
      app/src/main/res/values/colors.xml
  81. 7 0
      app/src/main/res/values/strings.xml
  82. 16 0
      app/src/main/res/values/themes.xml
  83. 17 0
      app/src/test/java/io/anyrtc/liveplayer/ExampleUnitTest.java
  84. 19 0
      build.gradle
  85. 21 0
      gradle.properties
  86. BIN
      gradle/wrapper/gradle-wrapper.jar
  87. 6 0
      gradle/wrapper/gradle-wrapper.properties
  88. 185 0
      gradlew
  89. 89 0
      gradlew.bat
  90. 1 0
      liveplayer/.gitignore
  91. 52 0
      liveplayer/build.gradle
  92. 2 0
      liveplayer/consumer-rules.pro
  93. 21 0
      liveplayer/proguard-rules.pro
  94. 26 0
      liveplayer/src/androidTest/java/io/anyrtc/live/ExampleInstrumentedTest.java
  95. 33 0
      liveplayer/src/main/AndroidManifest.xml
  96. 138 0
      liveplayer/src/main/cpp/CMakeLists.txt
  97. 866 0
      liveplayer/src/main/cpp/jni/LiveEngine.cpp
  98. 15 0
      liveplayer/src/main/cpp/jni/PlatformContext.h
  99. 132 0
      liveplayer/src/main/cpp/jni/StaticThreads.cpp
  100. 36 0
      liveplayer/src/main/cpp/jni/StaticThreads.h

+ 15 - 0
.gitignore

@@ -0,0 +1,15 @@
+*.iml
+.gradle
+/local.properties
+/.idea/caches
+/.idea/libraries
+/.idea/modules.xml
+/.idea/workspace.xml
+/.idea/navEditor.xml
+/.idea/assetWizardSettings.xml
+.DS_Store
+/build
+/captures
+.externalNativeBuild
+.cxx
+local.properties

+ 3 - 0
.idea/.gitignore

@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml

+ 6 - 0
.idea/compiler.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="CompilerConfiguration">
+    <bytecodeTargetLevel target="11" />
+  </component>
+</project>

+ 18 - 0
.idea/deploymentTargetSelector.xml

@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="deploymentTargetSelector">
+    <selectionStates>
+      <SelectionState runConfigName="app">
+        <option name="selectionMode" value="DROPDOWN" />
+        <DropdownSelection timestamp="2024-12-11T08:23:36.198647700Z">
+          <Target type="DEFAULT_BOOT">
+            <handle>
+              <DeviceId pluginId="Default" identifier="serial=192.168.18.136:5555;connection=9cab0503" />
+            </handle>
+          </Target>
+        </DropdownSelection>
+        <DialogSelection />
+      </SelectionState>
+    </selectionStates>
+  </component>
+</project>

+ 20 - 0
.idea/gradle.xml

@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="GradleMigrationSettings" migrationVersion="1" />
+  <component name="GradleSettings">
+    <option name="linkedExternalProjectsSettings">
+      <GradleProjectSettings>
+        <option name="externalProjectPath" value="$PROJECT_DIR$" />
+        <option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
+        <option name="modules">
+          <set>
+            <option value="$PROJECT_DIR$" />
+            <option value="$PROJECT_DIR$/app" />
+            <option value="$PROJECT_DIR$/liveplayer" />
+          </set>
+        </option>
+        <option name="resolveExternalAnnotations" value="false" />
+      </GradleProjectSettings>
+    </option>
+  </component>
+</project>

+ 6 - 0
.idea/kotlinc.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="KotlinJpsPluginSettings">
+    <option name="version" value="1.6.21" />
+  </component>
+</project>

+ 10 - 0
.idea/migrations.xml

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectMigrations">
+    <option name="MigrateToGradleLocalJavaHome">
+      <set>
+        <option value="$PROJECT_DIR$" />
+      </set>
+    </option>
+  </component>
+</project>

+ 10 - 0
.idea/misc.xml

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ExternalStorageConfigurationManager" enabled="true" />
+  <component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="corretto-11" project-jdk-type="JavaSDK">
+    <output url="file://$PROJECT_DIR$/build/classes" />
+  </component>
+  <component name="ProjectType">
+    <option name="id" value="Android" />
+  </component>
+</project>

+ 6 - 0
.idea/vcs.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="VcsDirectoryMappings">
+    <mapping directory="$PROJECT_DIR$/.." vcs="Git" />
+  </component>
+</project>

+ 35 - 0
README.md

@@ -0,0 +1,35 @@
+<p align="left">
+<a href="https://chromium.googlesource.com/external/webrtc/+/branch-heads/4515"><img src="https://img.shields.io/badge/libwebrtc-m93.4577-blue.svg"/></a>
+<img src="https://img.shields.io/badge/ffmpeg-4.2-brightgreen"/>
+<img src="https://img.shields.io/badge/minSdk-19-orange"/>
+<img src="https://img.shields.io/badge/NDK-20.0.5594570-lightgrey">
+</p>
+
+## 快速体验🔜
+
+点击下载[Apk](https://www.pgyer.com/qxPXQJiY)
+
+<img src="https://www.pgyer.com/app/qrcode/qxPXQJiY">
+
+
+## Run
+
+下载本工程,使用 AndroidStudio 打开之前,需要下载 webRTC,ffmpeg库。
+
+下载地址:👉[Lib](https://storage.agrtc.cn:1000/share/0v2et4RX)
+
+下载解压缩后将lib文件夹移至:**liveplayer/src/main/cpp** 目录下即可
+
+## ⚠️
+
+请使用 **NDK 版本:20.0.5594570** 编译,否则可能会出现各种错误🙅‍
+
+```cpp
+ld: error: duplicate symbol: webrtc::CreatePeerConnectionFactory
+  >>> defined at create_peerconnection_factory.cc:41 (../../api/create_peerconnection_factory.cc:41)
+  >>>            create_peerconnection_factory.o:
+  >>> defined at create_peerconnection_factory.cc:41 (../../api/create_peerconnection_factory.cc:41)
+  >>>            create_peerconnection_factory.o:
+  
+```
+

+ 1 - 0
app/.gitignore

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

+ 65 - 0
app/build.gradle

@@ -0,0 +1,65 @@
+plugins {
+    id 'com.android.application'
+    id 'kotlin-android'
+}
+
+
+android {
+    compileSdk 31
+
+    defaultConfig {
+        applicationId "io.anyrtc.liveplayer"
+        minSdk 21
+        targetSdk 31
+        versionCode 1
+        versionName "1.0"
+
+        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+    }
+
+    buildTypes {
+        release {
+            minifyEnabled true
+            shrinkResources true
+            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+        }
+        debug {
+            minifyEnabled false
+            debuggable true
+            jniDebuggable true
+            renderscriptDebuggable true
+        }
+    }
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_8
+        targetCompatibility JavaVersion.VERSION_1_8
+    }
+    kotlinOptions {
+        jvmTarget = '1.8'
+    }
+    viewBinding {
+        enabled = true
+    }
+    dataBinding {
+        enabled = true
+    }
+}
+
+dependencies {
+    implementation fileTree(dir: "libs", include: ["*.jar"])
+    implementation 'androidx.core:core-ktx:1.7.0'
+    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.1'
+    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2'
+    implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.0'
+    implementation 'androidx.appcompat:appcompat:1.4.1'
+    implementation 'com.google.android.material:material:1.5.0'
+    implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
+    implementation project(path: ':liveplayer')
+    testImplementation 'junit:junit:4.+'
+    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
+    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
+    implementation("io.coil-kt:coil-gif:1.4.0")
+    implementation("io.coil-kt:coil:1.4.0")
+    implementation 'com.github.LxzBUG:ScreenShare:1.1.6'
+
+}

+ 22 - 0
app/proguard-rules.pro

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

+ 26 - 0
app/src/androidTest/java/io/anyrtc/liveplayer/ExampleInstrumentedTest.java

@@ -0,0 +1,26 @@
+package io.anyrtc.liveplayer;
+
+import android.content.Context;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.*;
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+    @Test
+    public void useAppContext() {
+        // Context of the app under test.
+        Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        assertEquals("io.anyrtc.liveplayer", appContext.getPackageName());
+    }
+}

+ 37 - 0
app/src/main/AndroidManifest.xml

@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="io.anyrtc.liveplayer">
+
+    <application
+        android:allowBackup="true"
+        android:hardwareAccelerated="true"
+        android:icon="@mipmap/ic_launcher"
+        android:label="@string/app_name"
+        android:supportsRtl="true"
+        android:theme="@style/Theme.LivePlayer">
+        <activity
+            android:name=".PushActivity"
+            android:exported="true"
+            android:configChanges="keyboardHidden|orientation|screenSize"
+            android:hardwareAccelerated="true" />
+        <activity
+            android:name=".InputActivity"
+            android:screenOrientation="portrait"
+            android:exported="true" />
+        <activity
+            android:name=".PullActivity"
+            android:configChanges="keyboardHidden|orientation|screenSize"
+            android:exported="true" />
+        <activity
+            android:name=".MainActivity"
+            android:screenOrientation="portrait"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+
+</manifest>

+ 27 - 0
app/src/main/java/io/anyrtc/liveplayer/BaseActivity.kt

@@ -0,0 +1,27 @@
+package io.anyrtc.liveplayer
+
+import android.os.Bundle
+import androidx.appcompat.app.AppCompatActivity
+import java.math.BigDecimal
+import java.text.DecimalFormat
+
+open class BaseActivity : AppCompatActivity() {
+
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        ScreenUtils.adapterScreen(this, 375, false)
+    }
+
+    override fun onDestroy() {
+        super.onDestroy()
+      ScreenUtils.resetScreen(this)
+    }
+}
+
+fun Float.format():Float{
+    val value = BigDecimal(this.toString()).setScale(2, BigDecimal.ROUND_HALF_UP).toFloat()
+    val decimalFormat = DecimalFormat("0.00#")
+    val result = decimalFormat.format(value)
+    return result.toFloat()
+}

+ 161 - 0
app/src/main/java/io/anyrtc/liveplayer/Ext.kt

@@ -0,0 +1,161 @@
+package io.anyrtc.liveplayer
+
+import android.app.Activity
+import android.content.Context
+import android.content.Intent
+import android.content.res.Resources
+import android.os.Build
+import android.util.TypedValue
+import android.view.View
+import android.view.ViewGroup
+import android.view.WindowManager
+import android.widget.RelativeLayout
+import android.widget.Toast
+import androidx.annotation.ColorInt
+import androidx.annotation.ColorRes
+
+private const val COLOR_TRANSPARENT = 0
+
+public fun View.Gone() {
+    this.visibility = View.GONE
+}
+
+public fun View.Show() {
+    this.visibility = View.VISIBLE
+}
+
+public fun Activity.toast(msg: String) {
+    Toast.makeText(this, msg, Toast.LENGTH_SHORT).show()
+}
+
+public fun <T> Activity.go(clazz: Class<T>) {
+    startActivity(Intent(this, clazz))
+}
+
+public fun <T> Activity.go(clazz: Class<T>,vararg params:Pair<String,Any>) {
+    startActivity(Intent(this, clazz).apply {
+        params.forEach {
+            if (it.second is Int) {
+                this.putExtra(it.first, it.second as Int)
+            }else {
+                this.putExtra(it.first, it.second as String)
+            }
+        }
+    })
+}
+
+
+fun Activity.immersiveRes(@ColorRes color: Int, darkMode: Boolean? = null) =
+    immersive(resources.getColor(color), darkMode)
+
+fun Activity.immersive(@ColorInt color: Int = COLOR_TRANSPARENT, darkMode: Boolean? = null) {
+    when {
+        Build.VERSION.SDK_INT >= 21 -> {
+            when (color) {
+                COLOR_TRANSPARENT -> {
+                    window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
+                    var systemUiVisibility = window.decorView.systemUiVisibility
+                    systemUiVisibility = systemUiVisibility or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+                    systemUiVisibility = systemUiVisibility or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+                    window.decorView.systemUiVisibility = systemUiVisibility
+                    window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
+                    window.statusBarColor = color
+                }
+                else -> {
+                    window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
+                    var systemUiVisibility = window.decorView.systemUiVisibility
+                    systemUiVisibility =
+                        systemUiVisibility and View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+                    systemUiVisibility = systemUiVisibility and View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+                    window.decorView.systemUiVisibility = systemUiVisibility
+                    window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
+                    window.statusBarColor = color
+                }
+            }
+        }
+        Build.VERSION.SDK_INT >= 19 -> {
+            window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
+            if (color != COLOR_TRANSPARENT) {
+                setTranslucentView(window.decorView as ViewGroup, color)
+            }
+        }
+    }
+    if (darkMode != null) {
+        darkMode(darkMode)
+    }
+}
+
+private fun Context.setTranslucentView(container: ViewGroup, color: Int) {
+    if (Build.VERSION.SDK_INT >= 19) {
+        var simulateStatusBar: View? = container.findViewById(android.R.id.custom)
+        if (simulateStatusBar == null && color != 0) {
+            simulateStatusBar = View(container.context)
+            simulateStatusBar.id = android.R.id.custom
+            val lp = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, statusBarHeight)
+            container.addView(simulateStatusBar, lp)
+        }
+        simulateStatusBar?.setBackgroundColor(color)
+    }
+}
+
+fun Activity.darkMode(darkMode: Boolean = true) {
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+        var systemUiVisibility = window.decorView.systemUiVisibility
+        systemUiVisibility = if (darkMode) {
+            systemUiVisibility or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
+        } else {
+            systemUiVisibility and View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.inv()
+        }
+        window.decorView.systemUiVisibility = systemUiVisibility
+    }
+}
+
+val Context?.statusBarHeight: Int
+    get() {
+        this ?: return 0
+        var result = 24
+        val resId = resources.getIdentifier("status_bar_height", "dimen", "android")
+        result = if (resId > 0) {
+            resources.getDimensionPixelSize(resId)
+        } else {
+            TypedValue.applyDimension(
+                TypedValue.COMPLEX_UNIT_DIP,
+                result.toFloat(), Resources.getSystem().displayMetrics
+            ).toInt()
+        }
+        return result
+    }
+
+fun View.statusPadding(remove: Boolean = false) {
+    if (this is RelativeLayout) {
+        throw UnsupportedOperationException("Unsupported set statusPadding for RelativeLayout")
+    }
+    if (Build.VERSION.SDK_INT >= 19) {
+        val statusBarHeight = context.statusBarHeight
+        val lp = layoutParams
+        if (lp != null && lp.height > 0) {
+            lp.height += statusBarHeight //增高
+        }
+        if (remove) {
+            if (paddingTop < statusBarHeight) return
+            setPadding(
+                paddingLeft, paddingTop - statusBarHeight,
+                paddingRight, paddingBottom
+            )
+        } else {
+            if (paddingTop >= statusBarHeight) return
+            setPadding(
+                paddingLeft, paddingTop + statusBarHeight,
+                paddingRight, paddingBottom
+            )
+        }
+    }
+}
+
+fun Collection<Boolean>.isAllGranted():Boolean{
+    var count = 0
+    this.forEach {
+        if (it) count++
+    }
+    return count==this.size
+}

+ 105 - 0
app/src/main/java/io/anyrtc/liveplayer/InputActivity.kt

@@ -0,0 +1,105 @@
+package io.anyrtc.liveplayer
+
+import android.content.Intent
+import androidx.appcompat.app.AppCompatActivity
+import android.os.Bundle
+import android.text.TextUtils
+import android.view.View
+import android.widget.RadioGroup
+import android.widget.Toast
+import androidx.activity.result.ActivityResultLauncher
+import androidx.activity.result.ActivityResultRegistry
+import androidx.activity.result.contract.ActivityResultContracts
+import androidx.databinding.BaseObservable
+import androidx.databinding.ObservableInt
+import io.anyrtc.liveplayer.databinding.ActivityInputBinding
+
+class InputActivity : BaseActivity(),RadioGroup.OnCheckedChangeListener,View.OnClickListener{
+
+    private val binding by lazy { ActivityInputBinding.inflate(layoutInflater) }
+
+    private var pushType = 0 //0 camera 1 screen
+    private var resolution = 0 //0-720 1-540 2-360
+    private val config = Config()
+    private var requestPermission: ActivityResultLauncher<Array<String>>?=null
+    val VIDEO_1 = "https://www.apple.com/105/media/us/iphone-x/2017/01df5b43-28e4-4848-bf20-490c34a926a7/films/feature/iphone-x-feature-tpl-cc-us-20170912_1920x1080h.mp4"
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        setContentView(binding.root)
+        binding.config = config
+        immersiveRes(R.color.white,true)
+
+        val type = intent.getIntExtra("type",0)
+        config.type.set(type)
+
+         requestPermission = registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()){
+            if (it.values.isAllGranted()){
+                go(PushActivity::class.java, Pair("url",binding.etUrl.text.toString()),Pair("pushType",pushType),Pair("resolution",resolution))
+            }else{
+                toast("请打开相关权限")
+            }
+        }
+        binding.run {
+            rgType.check(R.id.rb_camera)
+            rgResolution.check(R.id.rb_a)
+            btnStart.setOnClickListener(this@InputActivity)
+            rgType.setOnCheckedChangeListener(this@InputActivity)
+            rgResolution.setOnCheckedChangeListener(this@InputActivity)
+            imgBack.setOnClickListener { finish() }
+
+        }
+
+    }
+
+    inner class Config  {
+        var type = ObservableInt(0)
+    }
+
+    override fun onCheckedChanged(group: RadioGroup?, checkedId: Int) {
+        when(checkedId){
+            R.id.rb_a->{
+                this@InputActivity.resolution=0
+            }
+            R.id.rb_b->{
+                this@InputActivity.resolution=1
+            }
+            R.id.rb_c->{
+                this@InputActivity.resolution=2
+            }
+            R.id.rb_aa->{
+                this@InputActivity.resolution=3
+            }
+            R.id.rb_camera-> {
+                this@InputActivity.pushType = 0
+            }
+            R.id.rb_screen->{
+                this@InputActivity.pushType = 1
+            }
+            else->{
+                this@InputActivity.pushType = 2
+            }
+        }
+    }
+
+    override fun onClick(v: View?) {
+        when(v?.id){
+            R.id.btn_start->{
+                if (TextUtils.isEmpty(binding.etUrl.text.toString())){
+                    toast("请输入推/拉流地址")
+                    return
+                }
+                when(config.type.get()){
+                    0->{
+                        requestPermission?.launch(arrayOf(android.Manifest.permission.CAMERA,android.Manifest.permission.RECORD_AUDIO))
+                    }
+                    1->{
+                        go(PullActivity::class.java, Pair("url",VIDEO_1))
+                    }
+                }
+            }
+        }
+    }
+
+
+}

+ 64 - 0
app/src/main/java/io/anyrtc/liveplayer/MainActivity.kt

@@ -0,0 +1,64 @@
+package io.anyrtc.liveplayer
+
+import android.Manifest
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.os.Bundle
+import android.os.Handler
+import android.widget.Toast
+import androidx.core.app.ActivityCompat
+import androidx.core.content.ContextCompat
+import io.anyrtc.liveplayer.databinding.ActivityMainBinding
+
+
+class MainActivity : BaseActivity() {
+
+    private val binding by lazy { ActivityMainBinding.inflate(layoutInflater) }
+    private val REQUEST_BLUETOOTH_CONNECT_PERMISSION = 1
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        setContentView(binding.root)
+
+        // 检查权限是否已被授予
+        if (ContextCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_CONNECT)
+            != PackageManager.PERMISSION_GRANTED) {
+            // 如果未授予权限,则请求权限
+            ActivityCompat.requestPermissions(
+                this,
+                arrayOf(Manifest.permission.BLUETOOTH_CONNECT),
+                REQUEST_BLUETOOTH_CONNECT_PERMISSION
+            )
+        }
+
+        immersiveRes(R.color.white,true)
+        binding.run {
+            cardPush.setOnClickListener {
+                go(InputActivity::class.java, Pair("type",0))
+            }
+            cardPull.setOnClickListener {
+                go(InputActivity::class.java,Pair("type",1))
+            }
+            cardLive.setOnClickListener {
+                toast("敬请期待")
+            }
+        }
+
+    }
+
+    override fun onRequestPermissionsResult(
+        requestCode: Int,
+        permissions: Array<out String>,
+        grantResults: IntArray
+    ) {
+        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
+        if (requestCode == REQUEST_BLUETOOTH_CONNECT_PERMISSION) {
+            if ((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
+                // 权限授予成功
+                Toast.makeText(this, "蓝牙权限已授予", Toast.LENGTH_SHORT).show()
+            } else {
+                // 权限被拒绝
+                Toast.makeText(this, "需要蓝牙权限以正常运行", Toast.LENGTH_SHORT).show()
+            }
+        }
+    }
+}

+ 186 - 0
app/src/main/java/io/anyrtc/liveplayer/PullActivity.kt

@@ -0,0 +1,186 @@
+package io.anyrtc.liveplayer
+
+import android.animation.Animator
+import android.animation.AnimatorSet
+import android.animation.ObjectAnimator
+import android.graphics.Bitmap
+import androidx.appcompat.app.AppCompatActivity
+import android.os.Bundle
+import android.os.SystemClock
+import android.util.Log
+import android.view.animation.AnimationSet
+import androidx.databinding.ObservableField
+
+import io.anyrtc.live.*
+import io.anyrtc.live.internal.VideoCapturerDevice
+import io.anyrtc.live.util.ArJavaI420Buffer
+import io.anyrtc.liveplayer.databinding.ActivityPullBinding
+import org.webrtc.*
+import java.nio.ByteBuffer
+import java.util.concurrent.TimeUnit
+import android.view.animation.AccelerateDecelerateInterpolator
+
+
+
+
+class PullActivity : AppCompatActivity() {
+
+    private val binding by lazy { ActivityPullBinding.inflate(layoutInflater) }
+    private val liveEngine by lazy { ArLiveEngine.create(this) }
+    private val player by lazy { liveEngine.createArLivePlayer() }
+    private val playStatus = PlayStatus()
+    private var displayMode = ArLiveDef.ArLiveFillMode.ArLiveFillModeFit
+    private val setAnimator = AnimatorSet()
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        setContentView(binding.root)
+        binding.playStatus = playStatus
+        binding.root.statusPadding()
+        immersive()
+        player.setRenderView(binding.playView)
+        player.setRenderFillMode(displayMode)
+        var url = intent.getStringExtra("url")
+        Log.d("TAG123", "url:$url")
+        url = "rtmp://192.168.18.106"
+        player.setPlayMode(ArLiveDef.ArLivePlayMode.ArLivePlayModeLive)
+        player.setObserver(object :ArLivePlayerObserver(){
+            override fun onAudioPlayStatusUpdate(
+                player: ArLivePlayer?,
+                status: ArLiveDef.ArLivePlayStatus?,
+                reason: ArLiveDef.ArLiveStatusChangeReason?,
+                extraInfo: Bundle?
+            ) {
+                super.onAudioPlayStatusUpdate(player, status, reason, extraInfo)
+                when(status){
+                    ArLiveDef.ArLivePlayStatus.ArLivePlayStatusLoading->{
+                        playStatus.status.set("缓冲中....")
+                    }
+                    ArLiveDef.ArLivePlayStatus.ArLivePlayStatusPlaying->{
+                        playStatus.status.set("播放中....")
+                    }
+                    ArLiveDef.ArLivePlayStatus.ArLivePlayStatusStopped->{
+                        playStatus.status.set("已停止....")
+                    }
+                }
+
+            }
+
+            override fun onError(
+                player: ArLivePlayer?,
+                code: Int,
+                msg: String?,
+                extraInfo: Bundle?
+            ) {
+                super.onError(player, code, msg, extraInfo)
+            }
+
+            override fun onPlayoutVolumeUpdate(player: ArLivePlayer?, volume: Int) {
+                super.onPlayoutVolumeUpdate(player, volume)
+            }
+
+            override fun onReceiveSeiMessage(
+                player: ArLivePlayer?,
+                payloadType: Int,
+                data: ByteArray?
+            ) {
+                super.onReceiveSeiMessage(player, payloadType, data)
+
+            }
+
+            override fun onRenderVideoFrame(
+                player: ArLivePlayer?,
+                videoFrame: ArLiveDef.ArLiveVideoFrame?
+            ) {
+                super.onRenderVideoFrame(player, videoFrame)
+            }
+
+            override fun onSnapshotComplete(player: ArLivePlayer?, image: Bitmap?) {
+                super.onSnapshotComplete(player, image)
+                setAnimator.cancel()
+                binding.ivSnapResult.setImageBitmap(image)
+                val animation1 = ObjectAnimator.ofFloat(binding.ivSnapResult,"alpha",0f,1f)
+                animation1.setDuration(300)
+                val animation2 = ObjectAnimator.ofFloat(binding.ivSnapResult,"translationX",0f,binding.ivSnapResult.width.toFloat())
+                setAnimator.play(animation2).after(animation1).after(1600)
+                setAnimator.setInterpolator(AccelerateDecelerateInterpolator())
+                setAnimator.addListener(object :Animator.AnimatorListener{
+                    override fun onAnimationStart(animation: Animator?) {
+                    }
+
+                    override fun onAnimationEnd(animation: Animator?) {
+                        binding.ivSnapResult.alpha = 0f
+                        binding.ivSnapResult.translationX= 0f
+                    }
+
+                    override fun onAnimationCancel(animation: Animator?) {
+                    }
+
+                    override fun onAnimationRepeat(animation: Animator?) {
+                    }
+
+                })
+                setAnimator.start()
+            }
+
+            override fun onStatisticsUpdate(
+                player: ArLivePlayer?,
+                statistics: ArLiveDef.ArLivePlayerStatistics?
+            ) {
+                super.onStatisticsUpdate(player, statistics)
+            }
+
+            override fun onVideoPlayStatusUpdate(
+                player: ArLivePlayer?,
+                status: ArLiveDef.ArLivePlayStatus?,
+                reason: ArLiveDef.ArLiveStatusChangeReason?,
+                extraInfo: Bundle?
+            ) {
+                super.onVideoPlayStatusUpdate(player, status, reason, extraInfo)
+            }
+
+            override fun onWarning(
+                player: ArLivePlayer?,
+                code: Int,
+                msg: String?,
+                extraInfo: Bundle?
+            ) {
+                super.onWarning(player, code, msg, extraInfo)
+            }
+        })
+        player.setCacheParams(1f,2f)
+        player.startPlay(url)
+        binding.run {
+            binding.btnExit.setOnClickListener {
+                ArLiveEngine.release()
+                finish()
+            }
+
+            ivSnap.setOnClickListener {
+                player.snapshot()
+            }
+
+            tvMode.setOnClickListener {
+                if (displayMode == ArLiveDef.ArLiveFillMode.ArLiveFillModeFit){
+                    displayMode = ArLiveDef.ArLiveFillMode.ArLiveFillModeFill
+                }else{
+                    displayMode = ArLiveDef.ArLiveFillMode.ArLiveFillModeFit
+                }
+                player.setRenderFillMode(displayMode)
+                playStatus?.mode?.set(if (displayMode == ArLiveDef.ArLiveFillMode.ArLiveFillModeFit) "Fit 比例显示" else "Fill 填充")
+            }
+        }
+
+    }
+
+    override fun onBackPressed() {
+        ArLiveEngine.release()
+        finish()
+    }
+
+    inner class PlayStatus{
+        var status = ObservableField("")
+        var mode = ObservableField("Fit 比例显示")
+    }
+
+}

+ 206 - 0
app/src/main/java/io/anyrtc/liveplayer/PushActivity.kt

@@ -0,0 +1,206 @@
+package io.anyrtc.liveplayer
+
+import android.content.ClipData
+import android.content.ClipboardManager
+import android.content.Context
+import android.os.Build.VERSION.SDK_INT
+import android.os.Bundle
+import android.view.KeyEvent
+import android.view.View
+import android.widget.Toast
+import coil.ImageLoader
+import coil.decode.GifDecoder
+import coil.decode.ImageDecoderDecoder
+import coil.load
+import io.anyrtc.live.ArLiveDef
+import io.anyrtc.live.ArLiveDef.ArLiveVideoResolution
+import io.anyrtc.live.ArLiveEngine
+import io.anyrtc.live.ArLivePusherObserver
+import io.anyrtc.liveplayer.databinding.ActivityPushBinding
+import org.loka.screensharekit.EncodeBuilder
+import org.loka.screensharekit.ScreenShareKit
+import org.loka.screensharekit.callback.AudioCallBack
+import org.loka.screensharekit.callback.RGBACallBack
+import org.loka.screensharekit.callback.StartCaptureCallback
+
+class PushActivity : BaseActivity() {
+    private val binding by lazy { ActivityPushBinding.inflate(layoutInflater) }
+    private val liveEngine by lazy { ArLiveEngine.create(this)}
+    private val pusher by lazy { liveEngine.createArLivePusher() }
+    private var pushUrl = ""
+    private var pushType = 0
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        setContentView(binding.root)
+        immersive(darkMode = false)
+        pushType = intent.getIntExtra("pushType",0);
+        val resolution = intent.getIntExtra("resolution",0)
+        pushUrl = intent.getStringExtra("url").toString()
+        when(resolution){
+            0-> {
+                pusher.setVideoQuality(ArLiveDef.ArLiveVideoEncoderParam(ArLiveVideoResolution.ArLiveVideoResolution1280x720))
+            }
+            1->{
+                pusher.setVideoQuality(ArLiveDef.ArLiveVideoEncoderParam(ArLiveVideoResolution.ArLiveVideoResolution960x540))
+            }
+            2->{
+                pusher.setVideoQuality(ArLiveDef.ArLiveVideoEncoderParam(ArLiveVideoResolution.ArLiveVideoResolution640x360))
+            }
+            3->{
+                pusher.setVideoQuality(ArLiveDef.ArLiveVideoEncoderParam(ArLiveVideoResolution.ArLiveVideoResolution1920x1080))
+            }
+        }
+
+        if (pushType == 0){
+            pusher.setRenderView(binding.videoView)
+            pusher.startCamera(true)
+            pusher.startMicrophone()
+            pusher.startPush(pushUrl)
+        }else if (pushType == 1){
+            binding.ivBeauty.visibility = View.GONE
+            val imageLoader = ImageLoader.Builder(this)
+                .componentRegistry {
+                    if (SDK_INT >= 28) {
+                        add(ImageDecoderDecoder(this@PushActivity))
+                    } else {
+                        add(GifDecoder())
+                    }
+                }
+                .build()
+            binding.ivGif.load("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fhbimg.b0.upaiyun.com%2F60ed9921abb8a968651aae697626dc816624cc4770c32-uwUmhP_fw658&refer=http%3A%2F%2Fhbimg.b0.upaiyun.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1645166781&t=ff39058ea3e782746361d8b2bea68511",imageLoader)
+            pusher.startScreenCapture()
+	        pusher.startMicrophone()
+            pusher.startPush(pushUrl)
+        }else{//自定义音视频采集
+            pusher.enableCustomAudioCapture(true)
+            pusher.enableCustomVideoCapture(true)
+            ScreenShareKit.init(this).config(screenDataType = EncodeBuilder.SCREEN_DATA_TYPE.RGBA, audioCapture = true)
+                .onRGBA(object :RGBACallBack{
+                    override fun onRGBA(
+                        rgba: ByteArray,
+                        width: Int,
+                        height: Int,
+                        stride: Int,
+                        rotation: Int,
+                        rotationChanged: Boolean
+                    ) {
+                        pusher.sendCustomVideoFrame(ArLiveDef.ArLiveVideoFrame().apply {
+                            pixelFormat = ArLiveDef.ArLivePixelFormat.ArLivePixelFormatBGRA32
+                            bufferType = ArLiveDef.ArLiveBufferType.ArLiveBufferTypeByteArray
+                            data = rgba
+                            this.width = width
+                            this.height = height
+                            this.rotation = rotation
+                            this.stride = stride*4
+                        })
+                    }
+
+                })
+                .onAudio(object :AudioCallBack{
+                    override fun onAudio(buffer: ByteArray?, ts: Long) {
+                        pusher.sendCustomAudioFrame(ArLiveDef.ArLiveAudioFrame().apply {
+                            data = buffer
+                            sampleRate = 16000
+                            channel = 2
+                        })
+                    }
+
+                })
+                .onStart(object :StartCaptureCallback{
+                    override fun onStart() {
+                        pusher.startPush(pushUrl)
+                    }
+
+                })
+                .start()
+
+        }
+        initView()
+
+    }
+
+    private fun initView(){
+        binding.run {
+            tvUrl.text = pushUrl
+            ivExit.setOnClickListener {
+                if (pushType==2){
+                    ScreenShareKit.stop()
+                }
+                ArLiveEngine.release()
+                finish()
+            }
+            ivSwitch.setOnClickListener {
+                pusher.deviceManager.switchCamera()
+            }
+            tvUrl.setOnClickListener {
+                val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
+                val clip: ClipData = ClipData.newPlainText("pushUrl",pushUrl)
+                clipboard.setPrimaryClip(clip)
+                Toast.makeText(this@PushActivity, "已复制", Toast.LENGTH_SHORT).show()
+            }
+            ivBeauty.setOnClickListener {
+                ivBeauty.isSelected = !ivBeauty.isSelected
+                pusher.beautyManager.setBeautyEffect(ivBeauty.isSelected)
+            }
+            ivAudio.setOnClickListener {
+                ivAudio.isSelected = !ivAudio.isSelected
+                if (ivAudio.isSelected){
+                    pusher.pauseAudio()
+                }else{
+                    pusher.resumeAudio()
+                }
+            }
+            ivVideo.setOnClickListener {
+                ivVideo.isSelected = !ivVideo.isSelected
+                if (ivVideo.isSelected){
+                    pusher.pauseVideo()
+                }else{
+                    pusher.resumeVideo()
+                }
+            }
+
+            ivMirro.setOnClickListener {
+                ivMirro.isSelected = !ivMirro.isSelected
+                pusher.setRenderMirror(if (ivMirro.isSelected)ArLiveDef.ArLiveMirrorType.ArLiveMirrorTypeEnable else ArLiveDef.ArLiveMirrorType.ArLiveMirrorTypeDisable)
+                pusher.setEncoderMirror(ivMirro.isSelected)
+            }
+
+            pusher.setObserver(object :ArLivePusherObserver(){
+                override fun onPushStatusUpdate(
+                    status: ArLiveDef.ArLivePushStatus?,
+                    msg: String?,
+                    extraInfo: Bundle?
+                ) {
+                    super.onPushStatusUpdate(status, msg, extraInfo)
+                    when(status){
+                        ArLiveDef.ArLivePushStatus.ArLivePushStatusConnecting->{
+                            tvState.setText("连接中....")
+                        }
+                        ArLiveDef.ArLivePushStatus.ArLivePushStatusConnectSuccess->{
+                            tvState.setText("连接成功....")
+                        }
+                        ArLiveDef.ArLivePushStatus.ArLivePushStatusDisconnected->{
+                            tvState.setText("连接断开....")
+                        }
+                        ArLiveDef.ArLivePushStatus.ArLivePushStatusReconnecting->{
+                            tvState.setText("重连中....")
+                        }
+                    }
+                }
+            })
+
+        }
+    }
+
+    override fun onBackPressed() {
+        if (pushType==2){
+            ScreenShareKit.stop()
+        }
+        ArLiveEngine.release()
+        finish()
+    }
+
+
+
+}

+ 92 - 0
app/src/main/java/io/anyrtc/liveplayer/RecordThread.java

@@ -0,0 +1,92 @@
+package io.anyrtc.liveplayer;
+
+import android.Manifest;
+import android.content.pm.PackageManager;
+import android.media.AudioFormat;
+import android.media.AudioRecord;
+import android.media.MediaRecorder;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.core.app.ActivityCompat;
+
+
+public class RecordThread extends Thread {
+
+    private RecordDataCallback callback;
+
+
+    public interface RecordDataCallback{
+       void onFrame(byte[] audioFrame);
+    }
+
+
+    private volatile boolean stopped;
+    private RecordThread thread;
+    private AudioRecord audioRecord;
+    public static final int DEFAULT_SAMPLE_RATE = 48000;
+    /**
+     * 1 corresponds to AudioFormat.CHANNEL_IN_MONO;
+     * 2 corresponds to AudioFormat.CHANNEL_IN_STEREO
+     */
+    public static final int DEFAULT_CHANNEL_COUNT = 1, DEFAULT_CHANNEL_CONFIG = AudioFormat.CHANNEL_IN_MONO;
+    private byte[] buffer;
+
+    RecordThread(String name, RecordDataCallback callback) {
+        this.callback = callback;
+        int bufferSize = AudioRecord.getMinBufferSize(DEFAULT_SAMPLE_RATE, DEFAULT_CHANNEL_CONFIG,
+                AudioFormat.ENCODING_PCM_16BIT);
+        audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, DEFAULT_SAMPLE_RATE, DEFAULT_CHANNEL_COUNT,
+                AudioFormat.ENCODING_PCM_16BIT, bufferSize);
+        buffer = new byte[bufferSize];
+    }
+
+    @Override
+    public void run() {
+        try {
+            audioRecord.startRecording();
+            while (!stopped) {
+                int result = audioRecord.read(buffer, 0, buffer.length);
+                if (result >= 0) {
+                    callback.onFrame(buffer);
+                } else {
+                    logRecordError(result);
+                }
+                Log.d("MainActivity", "byte size is :" + result);
+            }
+            release();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+    private void release()
+    {
+        if (audioRecord != null)
+        {
+            audioRecord.stop();
+            buffer = null;
+        }
+    }
+
+    private void logRecordError(int error)
+    {
+        String message = "";
+        switch (error)
+        {
+            case AudioRecord.ERROR:
+                message = "generic operation failure";
+                break;
+            case AudioRecord.ERROR_BAD_VALUE:
+                message = "failure due to the use of an invalid value";
+                break;
+            case AudioRecord.ERROR_DEAD_OBJECT:
+                message = "object is no longer valid and needs to be recreated";
+                break;
+            case AudioRecord.ERROR_INVALID_OPERATION:
+                message = "failure due to the improper use of method";
+                break;
+        }
+        Log.e("MainActivity", message);
+    }
+
+}

+ 34 - 0
app/src/main/java/io/anyrtc/liveplayer/ScreenUtils.kt

@@ -0,0 +1,34 @@
+package io.anyrtc.liveplayer
+
+import android.app.Activity
+import android.content.res.Resources
+
+object ScreenUtils {
+
+    fun adapterScreen(activity: Activity, targetDP: Int, isVertical: Boolean) {
+        val sysDisplayMetrics = Resources.getSystem().displayMetrics
+        val activityDisplayMetrics = activity.resources.displayMetrics
+
+        if (isVertical) {
+            activityDisplayMetrics.density = activityDisplayMetrics.heightPixels / targetDP.toFloat()
+        } else {
+            activityDisplayMetrics.density = activityDisplayMetrics.widthPixels / targetDP.toFloat()
+        }
+        activityDisplayMetrics.scaledDensity = activityDisplayMetrics.density * (sysDisplayMetrics.scaledDensity / sysDisplayMetrics.density)
+        activityDisplayMetrics.densityDpi = (160 * activityDisplayMetrics.density).toInt()
+    }
+
+    fun resetScreen(activity: Activity) {
+        val sysDisplayMetrics = Resources.getSystem().displayMetrics
+        val appDisplayMetrics = activity.application.resources.displayMetrics
+        val activityDisplayMetrics = activity.resources.displayMetrics
+
+        activityDisplayMetrics.density = sysDisplayMetrics.density
+        activityDisplayMetrics.scaledDensity = sysDisplayMetrics.scaledDensity
+        activityDisplayMetrics.densityDpi = sysDisplayMetrics.densityDpi
+
+        appDisplayMetrics.density = sysDisplayMetrics.density
+        appDisplayMetrics.scaledDensity = sysDisplayMetrics.scaledDensity
+        appDisplayMetrics.densityDpi = sysDisplayMetrics.densityDpi
+    }
+}

+ 5 - 0
app/src/main/res/color/color_options.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:color="#0241FF" android:state_checked="true"></item>
+    <item android:color="#878799" android:state_checked="false"></item>
+</selector>

BIN
app/src/main/res/drawable-xxhdpi/img_audio_normol.png


BIN
app/src/main/res/drawable-xxhdpi/img_audio_select.png


BIN
app/src/main/res/drawable-xxhdpi/img_beauty.png


BIN
app/src/main/res/drawable-xxhdpi/img_beauty_normol.png


BIN
app/src/main/res/drawable-xxhdpi/img_beauty_select.png


BIN
app/src/main/res/drawable-xxhdpi/img_copy.png


BIN
app/src/main/res/drawable-xxhdpi/img_dou.png


BIN
app/src/main/res/drawable-xxhdpi/img_exit.png


BIN
app/src/main/res/drawable-xxhdpi/img_fullscreen.png


BIN
app/src/main/res/drawable-xxhdpi/img_live.png


BIN
app/src/main/res/drawable-xxhdpi/img_mirro_normol.png


BIN
app/src/main/res/drawable-xxhdpi/img_mirro_select.png


BIN
app/src/main/res/drawable-xxhdpi/img_pause.png


BIN
app/src/main/res/drawable-xxhdpi/img_play.png


BIN
app/src/main/res/drawable-xxhdpi/img_pull.png


BIN
app/src/main/res/drawable-xxhdpi/img_push.png


BIN
app/src/main/res/drawable-xxhdpi/img_replay.png


BIN
app/src/main/res/drawable-xxhdpi/img_small_video.png


BIN
app/src/main/res/drawable-xxhdpi/img_snap.png


BIN
app/src/main/res/drawable-xxhdpi/img_switch.png


BIN
app/src/main/res/drawable-xxhdpi/img_switch_camera.png


BIN
app/src/main/res/drawable-xxhdpi/img_todo.png


BIN
app/src/main/res/drawable-xxhdpi/img_tou.png


BIN
app/src/main/res/drawable-xxhdpi/img_video_normol.png


BIN
app/src/main/res/drawable-xxhdpi/img_video_select.png


+ 170 - 0
app/src/main/res/drawable/ic_launcher_background.xml

@@ -0,0 +1,170 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="108dp"
+    android:height="108dp"
+    android:viewportHeight="108"
+    android:viewportWidth="108">
+    <path
+        android:fillColor="#3DDC84"
+        android:pathData="M0,0h108v108h-108z" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M9,0L9,108"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,0L19,108"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M29,0L29,108"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M39,0L39,108"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M49,0L49,108"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M59,0L59,108"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M69,0L69,108"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M79,0L79,108"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M89,0L89,108"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M99,0L99,108"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,9L108,9"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,19L108,19"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,29L108,29"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,39L108,39"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,49L108,49"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,59L108,59"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,69L108,69"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,79L108,79"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,89L108,89"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,99L108,99"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,29L89,29"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,39L89,39"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,49L89,49"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,59L89,59"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,69L89,69"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,79L89,79"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M29,19L29,89"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M39,19L39,89"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M49,19L49,89"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M59,19L59,89"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M69,19L69,89"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M79,19L79,89"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+</vector>

+ 6 - 0
app/src/main/res/drawable/img_back.xml

@@ -0,0 +1,6 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="16dp"
+    android:height="16dp" android:viewportWidth="1024" android:viewportHeight="1024">
+    <path
+        android:pathData="M532.53,904.82L139.51,511.8 532.53,118.78c12.26,-12.26 12.43,-32.89 -0.19,-45.51 -12.71,-12.71 -33,-12.7 -45.51,-0.19L75.17,484.74c-7.12,7.12 -10.16,17.07 -8.99,26.62 -1.5,9.76 1.51,20.01 8.99,27.49l411.66,411.66c12.26,12.26 32.89,12.43 45.51,-0.19 12.71,-12.71 12.7,-33 0.19,-45.51z"
+        android:fillColor="#1A1A1E" />
+</vector>

+ 9 - 0
app/src/main/res/drawable/img_exit.xml

@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="32dp"
+    android:height="32dp" android:viewportWidth="1024" android:viewportHeight="1024">
+    <path
+        android:pathData="M909.77,517.38L746.34,369.66a33.95,33.95 0,0 0,-45.77 0,26.73 26.73,0 0,0 0,41.37L808.45,508.52H494.59c-19.56,0 -32.67,11.83 -32.67,29.54 0,17.77 13.11,29.54 32.72,29.54H808.45L700.57,665.14a26.73,26.73 0,0 0,0 41.37,31.18 31.18,0 0,0 22.89,8.86 31.23,31.23 0,0 0,22.89 -8.86l163.43,-150.68s0,-2.97 3.28,-2.97a22.78,22.78 0,0 0,-3.28 -35.48z"
+        android:fillColor="#ffffff" />
+    <path
+        android:pathData="M102.4,827.19C102.4,880.69 145.66,921.6 202.24,921.6h566.02c56.58,0 99.84,-40.91 99.84,-94.41v-62.98c0,-18.84 -13.31,-31.44 -33.28,-31.44s-33.28,12.6 -33.28,31.49v62.98c0,18.84 -13.31,31.44 -33.28,31.44H202.24c-19.97,0 -33.28,-12.6 -33.28,-31.49V197.84c0,-18.89 13.31,-31.49 33.28,-31.49h565.96c19.97,0 33.28,12.6 33.28,31.49v62.92c0,18.89 13.31,31.49 33.28,31.49s33.28,-12.6 33.28,-31.49v-62.98C868.1,144.38 824.83,103.42 768.26,103.42H202.24C145.66,103.42 102.4,144.33 102.4,197.84"
+        android:fillColor="#ffffff" />
+</vector>

+ 5 - 0
app/src/main/res/drawable/select_audio.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@drawable/img_audio_normol" android:state_selected="true"/>
+    <item android:drawable="@drawable/img_audio_select" android:state_selected="false"/>
+</selector>

+ 5 - 0
app/src/main/res/drawable/select_beauty.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@drawable/img_beauty_normol" android:state_selected="false"/>
+    <item android:drawable="@drawable/img_beauty_select" android:state_selected="true"/>
+</selector>

+ 5 - 0
app/src/main/res/drawable/select_mirro.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@drawable/img_mirro_normol" android:state_selected="false"/>
+    <item android:drawable="@drawable/img_mirro_select" android:state_selected="true"/>
+</selector>

+ 5 - 0
app/src/main/res/drawable/select_options.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@drawable/shape_check" android:state_checked="true"></item>
+    <item android:drawable="@drawable/shape_normol" android:state_checked="false"></item>
+</selector>

+ 5 - 0
app/src/main/res/drawable/select_video.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@drawable/img_video_normol" android:state_selected="true"/>
+    <item android:drawable="@drawable/img_video_select" android:state_selected="false"/>
+</selector>

+ 5 - 0
app/src/main/res/drawable/shape_blue.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <solid android:color="#0241FF"></solid>
+    <corners android:radius="2dp"></corners>
+</shape>

+ 5 - 0
app/src/main/res/drawable/shape_check.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <stroke android:color="#0241FF" android:width="1dp"></stroke>
+    <solid android:color="@color/white"></solid>
+</shape>

+ 5 - 0
app/src/main/res/drawable/shape_edit.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <solid android:color="#FAFAFA"></solid>
+    <corners android:radius="2dp"></corners>
+</shape>

+ 5 - 0
app/src/main/res/drawable/shape_gray.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <solid android:color="#F2F3F5"></solid>
+    <corners android:radius="8dp"></corners>
+</shape>

+ 5 - 0
app/src/main/res/drawable/shape_normol.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <stroke android:color="#878799" android:width="1dp"></stroke>
+    <solid android:color="@color/white"></solid>
+</shape>

+ 5 - 0
app/src/main/res/drawable/shape_white.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <solid android:color="#CCffffff"></solid>
+    <corners android:radius="2dp"></corners>
+</shape>

+ 215 - 0
app/src/main/res/layout/activity_input.xml

@@ -0,0 +1,215 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools">
+
+    <data>
+        <import type="io.anyrtc.liveplayer.InputActivity.Config"/>
+        <import type="android.view.View"/>
+        <variable
+            name="config"
+            type="Config" />
+    </data>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="vertical"
+        android:background="@color/white"
+        tools:context=".InputActivity">
+
+
+        <RelativeLayout
+            android:layout_width="match_parent"
+            android:layout_height="45dp">
+
+            <ImageView
+                android:id="@+id/img_back"
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:layout_centerVertical="true"
+                android:paddingLeft="12dp"
+                android:src="@drawable/img_back" />
+
+            <TextView
+                android:id="@+id/tv_title"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_centerInParent="true"
+                android:text="@{config.type == 0 ? `直播推流` : `拉流播放`}"
+                android:textColor="@color/black"
+                android:textSize="16sp" />
+        </RelativeLayout>
+
+
+        <EditText
+            android:id="@+id/et_url"
+            android:layout_width="match_parent"
+            android:layout_height="42dp"
+            android:layout_marginLeft="16dp"
+            android:layout_marginRight="16dp"
+            android:layout_marginTop="40dp"
+            android:paddingLeft="12dp"
+            android:textSize="14sp"
+            android:singleLine="true"
+            android:maxLines="2"
+            android:gravity="left|center_vertical"
+            android:hint="@{config.type == 0 ? `请输入推流地址` : `请输入拉流地址`}"
+            android:background="@drawable/shape_edit"/>
+
+        <LinearLayout
+            android:id="@+id/ll_options"
+            android:visibility="@{config.type == 0 ? View.VISIBLE : View.GONE}"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_margin="16dp"
+            android:orientation="vertical">
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="分辨率"
+                android:textSize="12sp"
+                android:textColor="#1A1A1E"
+                android:textStyle="bold"/>
+
+            <RadioGroup
+                android:id="@+id/rg_resolution"
+                android:layout_width="match_parent"
+                android:layout_marginTop="12dp"
+                android:layout_height="wrap_content"
+                android:orientation="horizontal">
+                <RadioButton
+                    android:id="@+id/rb_aa"
+                    android:layout_width="0dp"
+                    android:layout_height="44dp"
+                    android:textColor="@color/color_options"
+                    android:button="@null"
+                    android:gravity="center"
+                    android:background="@drawable/select_options"
+                    android:layout_weight="1"
+                    android:text="1080P"/>
+                <Space
+                    android:layout_width="25dp"
+                    android:layout_height="wrap_content"/>
+
+                <RadioButton
+                    android:id="@+id/rb_a"
+                    android:layout_width="0dp"
+                    android:layout_height="44dp"
+                    android:textColor="@color/color_options"
+                    android:button="@null"
+                    android:gravity="center"
+                    android:background="@drawable/select_options"
+                    android:layout_weight="1"
+                    android:text="720P"/>
+
+                <Space
+                    android:layout_width="25dp"
+                    android:layout_height="wrap_content"/>
+
+                <RadioButton
+                    android:id="@+id/rb_b"
+                    android:layout_width="0dp"
+                    android:button="@null"
+                    android:text="540P"
+                    android:textColor="@color/color_options"
+                    android:background="@drawable/select_options"
+                    android:gravity="center"
+                    android:layout_height="44dp"
+                    android:layout_weight="1"/>
+
+                <Space
+                    android:layout_width="25dp"
+                    android:layout_height="wrap_content"/>
+
+                <RadioButton
+                    android:id="@+id/rb_c"
+                    android:layout_width="0dp"
+                    android:button="@null"
+                    android:text="360P"
+                    android:textColor="@color/color_options"
+                    android:background="@drawable/select_options"
+                    android:gravity="center"
+                    android:layout_height="44dp"
+                    android:layout_weight="1"/>
+            </RadioGroup>
+
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="类型"
+                android:layout_marginTop="20dp"
+                android:textSize="12sp"
+                android:textColor="#1A1A1E"
+                android:textStyle="bold"/>
+
+            <RadioGroup
+                android:id="@+id/rg_type"
+                android:layout_width="match_parent"
+                android:layout_marginTop="12dp"
+                android:layout_height="wrap_content"
+                android:orientation="horizontal">
+
+                <RadioButton
+                    android:id="@+id/rb_camera"
+                    android:layout_width="0dp"
+                    android:layout_height="44dp"
+                    android:button="@null"
+                    android:gravity="center"
+                    android:textColor="@color/color_options"
+                    android:background="@drawable/select_options"
+                    android:layout_weight="1"
+                    android:text="摄像头推流"/>
+
+                <Space
+                    android:layout_width="25dp"
+                    android:layout_height="wrap_content"/>
+
+                <RadioButton
+                    android:id="@+id/rb_screen"
+                    android:layout_width="0dp"
+                    android:button="@null"
+                    android:text="屏幕共享"
+                    android:textColor="@color/color_options"
+                    android:background="@drawable/select_options"
+                    android:gravity="center"
+                    android:layout_height="44dp"
+                    android:layout_weight="1"/>
+
+                <Space
+                    android:layout_width="25dp"
+                    android:layout_height="wrap_content"/>
+
+                <RadioButton
+                    android:id="@+id/rb_custom"
+                    android:layout_width="0dp"
+                    android:button="@null"
+                    android:text="自定义采集"
+                    android:textColor="@color/color_options"
+                    android:background="@drawable/select_options"
+                    android:gravity="center"
+                    android:layout_height="44dp"
+                    android:layout_weight="1"/>
+
+            </RadioGroup>
+
+        </LinearLayout>
+
+
+        <Button
+            android:id="@+id/btn_start"
+            android:layout_width="match_parent"
+            android:layout_height="44dp"
+            android:layout_marginLeft="16dp"
+            android:layout_marginRight="16dp"
+            android:layout_marginTop="40dp"
+            android:textSize="14sp"
+            android:textColor="@color/white"
+            android:gravity="center"
+            android:text="@{config.type ==0 ? `开始推流`:`开始播放`}"
+            android:background="@drawable/shape_blue"/>
+
+    </LinearLayout>
+</layout>

+ 206 - 0
app/src/main/res/layout/activity_main.xml

@@ -0,0 +1,206 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    android:background="@color/white"
+    tools:context=".MainActivity">
+
+
+    <androidx.cardview.widget.CardView
+        android:id="@+id/card_push"
+        android:layout_width="match_parent"
+        android:layout_height="88dp"
+        android:clickable="true"
+        app:cardCornerRadius="4dp"
+        android:foreground="?attr/selectableItemBackground"
+        android:layout_marginLeft="16dp"
+        android:layout_marginRight="16dp"
+        android:layout_marginTop="16dp">
+
+        <androidx.constraintlayout.widget.ConstraintLayout
+            android:layout_width="match_parent"
+            android:layout_height="match_parent">
+
+            <androidx.appcompat.widget.AppCompatImageView
+                android:id="@+id/img_push"
+                android:layout_width="56dp"
+                android:layout_height="56dp"
+                android:layout_marginLeft="16dp"
+                app:layout_constraintLeft_toLeftOf="parent"
+                app:layout_constraintTop_toTopOf="parent"
+                app:layout_constraintBottom_toBottomOf="parent"
+                app:srcCompat="@drawable/img_push"></androidx.appcompat.widget.AppCompatImageView>
+
+            <TextView
+                android:id="@+id/tv_push_title"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:textSize="14sp"
+                android:textStyle="bold"
+                android:text="直播推流"
+                android:layout_marginLeft="16dp"
+                app:layout_constraintVertical_chainStyle="packed"
+                app:layout_constraintBottom_toTopOf="@+id/tv_push_desc"
+                app:layout_constraintTop_toTopOf="parent"
+                app:layout_constraintLeft_toRightOf="@+id/img_push"
+                android:textColor="#1A1A1E">
+            </TextView>
+
+            <TextView
+                android:id="@+id/tv_push_desc"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:textSize="12sp"
+                android:layout_marginTop="4dp"
+                android:text="@string/push_desc"
+                android:layout_marginLeft="16dp"
+                app:layout_constraintTop_toBottomOf="@+id/tv_push_title"
+                app:layout_constraintLeft_toRightOf="@+id/img_push"
+                app:layout_constraintBottom_toBottomOf="parent"
+                android:textColor="#878799">
+            </TextView>
+
+        </androidx.constraintlayout.widget.ConstraintLayout>
+    </androidx.cardview.widget.CardView>
+
+    <androidx.cardview.widget.CardView
+        android:id="@+id/card_pull"
+        android:layout_width="match_parent"
+        android:layout_height="88dp"
+        android:clickable="true"
+        app:cardCornerRadius="4dp"
+        android:foreground="?attr/selectableItemBackground"
+        android:layout_marginLeft="16dp"
+        android:layout_marginRight="16dp"
+        android:layout_marginTop="16dp">
+
+        <androidx.constraintlayout.widget.ConstraintLayout
+            android:layout_width="match_parent"
+            android:layout_height="match_parent">
+
+            <androidx.appcompat.widget.AppCompatImageView
+                android:id="@+id/img_pull"
+                android:layout_width="56dp"
+                android:layout_height="56dp"
+                android:layout_marginLeft="16dp"
+                app:layout_constraintLeft_toLeftOf="parent"
+                app:layout_constraintTop_toTopOf="parent"
+                app:layout_constraintBottom_toBottomOf="parent"
+                app:srcCompat="@drawable/img_pull"></androidx.appcompat.widget.AppCompatImageView>
+
+            <TextView
+                android:id="@+id/tv_pull_title"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:textSize="14sp"
+                android:textStyle="bold"
+                android:text="直播拉流(播放)"
+                android:layout_marginLeft="16dp"
+                app:layout_constraintVertical_chainStyle="packed"
+                app:layout_constraintBottom_toTopOf="@+id/tv_pull_desc"
+                app:layout_constraintTop_toTopOf="parent"
+                app:layout_constraintLeft_toRightOf="@+id/img_pull"
+                android:textColor="#1A1A1E">
+            </TextView>
+
+            <TextView
+                android:id="@+id/tv_pull_desc"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:textSize="12sp"
+                android:layout_marginTop="4dp"
+                android:text="@string/pull_desc"
+                android:layout_marginLeft="16dp"
+                app:layout_constraintTop_toBottomOf="@+id/tv_pull_title"
+                app:layout_constraintLeft_toRightOf="@+id/img_pull"
+                app:layout_constraintBottom_toBottomOf="parent"
+                android:textColor="#878799">
+            </TextView>
+
+        </androidx.constraintlayout.widget.ConstraintLayout>
+    </androidx.cardview.widget.CardView>
+
+
+    <androidx.cardview.widget.CardView
+        android:id="@+id/card_live"
+        android:layout_width="match_parent"
+        android:layout_height="88dp"
+        android:clickable="true"
+        app:cardCornerRadius="4dp"
+        android:foreground="?attr/selectableItemBackground"
+        android:layout_marginLeft="16dp"
+        android:layout_marginRight="16dp"
+        android:layout_marginTop="16dp">
+
+        <androidx.constraintlayout.widget.ConstraintLayout
+            android:layout_width="match_parent"
+            android:layout_height="match_parent">
+
+            <androidx.appcompat.widget.AppCompatImageView
+                android:id="@+id/img_live"
+                android:layout_width="56dp"
+                android:layout_height="56dp"
+                android:layout_marginLeft="16dp"
+                app:layout_constraintLeft_toLeftOf="parent"
+                app:layout_constraintTop_toTopOf="parent"
+                app:layout_constraintBottom_toBottomOf="parent"
+                app:srcCompat="@drawable/img_live"></androidx.appcompat.widget.AppCompatImageView>
+
+            <TextView
+                android:id="@+id/tv_live_title"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:textSize="14sp"
+                android:textStyle="bold"
+                android:text="互动连麦"
+                android:layout_marginLeft="16dp"
+                app:layout_constraintVertical_chainStyle="packed"
+                app:layout_constraintBottom_toTopOf="@+id/tv_live_desc"
+                app:layout_constraintTop_toTopOf="parent"
+                app:layout_constraintLeft_toRightOf="@+id/img_live"
+                android:textColor="#1A1A1E">
+            </TextView>
+
+            <TextView
+                android:id="@+id/tv_live_desc"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:textSize="12sp"
+                android:layout_marginTop="4dp"
+                android:text="@string/live_desc"
+                android:layout_marginLeft="16dp"
+                app:layout_constraintTop_toBottomOf="@+id/tv_live_title"
+                app:layout_constraintLeft_toRightOf="@+id/img_live"
+                app:layout_constraintBottom_toBottomOf="parent"
+                android:textColor="#878799">
+            </TextView>
+
+            <androidx.appcompat.widget.AppCompatImageView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                app:layout_constraintRight_toRightOf="parent"
+                app:layout_constraintTop_toTopOf="parent"
+                app:layout_constraintBottom_toBottomOf="parent"
+                app:srcCompat="@drawable/img_todo"></androidx.appcompat.widget.AppCompatImageView>
+
+        </androidx.constraintlayout.widget.ConstraintLayout>
+    </androidx.cardview.widget.CardView>
+
+    <Space
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1">
+    </Space>
+
+    <TextView
+        android:layout_width="match_parent"
+        android:textColor="#C4C4CE"
+        android:text="Power by anyRTC"
+        android:textSize="12sp"
+        android:gravity="center"
+        android:layout_height="40dp"></TextView>
+
+</LinearLayout>

+ 110 - 0
app/src/main/res/layout/activity_pull.xml

@@ -0,0 +1,110 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools">
+
+    <data>
+        <import type="io.anyrtc.liveplayer.PullActivity.PlayStatus"></import>
+        <variable
+            name="playStatus"
+            type="PlayStatus" />
+
+    </data>
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:background="@color/black"
+        tools:context=".PullActivity">
+
+
+        <org.webrtc.TextureViewRenderer
+            android:id="@+id/play_view"
+            app:layout_constraintLeft_toLeftOf="parent"
+            app:layout_constraintRight_toRightOf="parent"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintBottom_toBottomOf="parent"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"></org.webrtc.TextureViewRenderer>
+
+        <TextView
+            android:id="@+id/tv_status"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textColor="@color/white"
+            android:text="@{`播放状态:`+playStatus.status}"
+            android:textSize="14sp"
+            app:layout_constraintLeft_toLeftOf="parent"
+            app:layout_constraintTop_toTopOf="parent"
+            android:layout_margin="24dp"></TextView>
+
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textColor="@color/white"
+            android:text="@{`显示模式:`+playStatus.mode}"
+            android:textSize="14sp"
+            app:layout_constraintLeft_toLeftOf="parent"
+            app:layout_constraintTop_toBottomOf="@+id/tv_status"
+            android:layout_margin="24dp"></TextView>
+
+
+        <ImageView
+            android:id="@+id/btn_exit"
+            android:layout_width="44dp"
+            android:layout_height="44dp"
+            android:background="@drawable/shape_white"
+            android:scaleType="center"
+            app:layout_constraintBottom_toBottomOf="parent"
+            android:src="@drawable/img_exit"
+            android:layout_alignParentBottom="true"
+            app:layout_constraintRight_toRightOf="parent"
+            android:layout_margin="16dp"/>
+
+        <ImageView
+            android:id="@+id/iv_snap_result"
+            android:layout_width="150dp"
+            android:layout_height="200dp"
+            android:layout_margin="8dp"
+            android:alpha="0"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintRight_toRightOf="parent">
+        </ImageView>
+
+        <LinearLayout
+            android:layout_width="164dp"
+            app:layout_constraintLeft_toLeftOf="parent"
+            app:layout_constraintBottom_toBottomOf="parent"
+            android:layout_height="44dp"
+            android:background="@drawable/shape_white"
+            android:layout_margin="16dp">
+
+            <TextView
+                android:id="@+id/tv_mode"
+                android:layout_width="0dp"
+                android:textColor="#0241FF"
+                android:layout_weight="1"
+                android:gravity="center"
+                android:layout_height="match_parent"
+                android:text="切换模式"/>
+
+            <View
+                android:layout_width="0.5dp"
+                android:layout_height="36dp"
+                android:layout_gravity="center_vertical"
+                android:background="#F2F3F5"/>
+
+            <ImageView
+                android:id="@+id/iv_snap"
+                android:layout_width="44dp"
+                android:layout_height="match_parent"
+                android:scaleType="center"
+                android:src="@drawable/img_snap"/>
+
+
+        </LinearLayout>
+
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+</layout>

+ 151 - 0
app/src/main/res/layout/activity_push.xml

@@ -0,0 +1,151 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@color/black"
+
+    tools:context=".PushActivity">
+
+    <org.webrtc.TextureViewRenderer
+        android:id="@+id/video_view"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_centerInParent="true"></org.webrtc.TextureViewRenderer>
+
+    <ImageView
+        android:id="@+id/iv_gif"
+        android:layout_centerInParent="true"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:scaleType="fitCenter">
+    </ImageView>
+    <LinearLayout
+        android:id="@+id/ll_url"
+        android:paddingTop="24dp"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal">
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textColor="@color/white"
+            android:layout_margin="16dp"
+            android:textSize="12sp"
+            android:text="推流地址:"></TextView>
+
+        <TextView
+            android:id="@+id/tv_url"
+            android:layout_width="0dp"
+            android:layout_weight="1"
+            android:gravity="right"
+            android:layout_height="wrap_content"
+            android:textColor="@color/white"
+            android:layout_margin="16dp"
+            android:textSize="12sp"
+            android:ellipsize="end"
+            android:singleLine="true"
+            android:drawablePadding="12dp"
+            android:layout_alignParentRight="true"
+            android:drawableRight="@drawable/img_copy"
+            android:text="rtmp:"></TextView>
+    </LinearLayout>
+    <LinearLayout
+        android:layout_below="@+id/ll_url"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal">
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textColor="@color/white"
+            android:layout_marginLeft="16dp"
+            android:textSize="12sp"
+            android:text="推流状态:"></TextView>
+
+        <TextView
+            android:id="@+id/tv_state"
+            android:layout_width="wrap_content"
+            android:layout_marginLeft="2dp"
+            android:layout_height="wrap_content"
+            android:textColor="#FFD600"
+            android:textSize="12sp"
+            android:ellipsize="end"
+            android:singleLine="true"
+            android:text="rtmp:"></TextView>
+    </LinearLayout>
+
+
+
+
+
+
+
+
+    <ImageView
+        android:id="@+id/iv_exit"
+        android:layout_width="44dp"
+        android:layout_height="44dp"
+        android:background="@drawable/shape_white"
+        android:scaleType="center"
+        android:src="@drawable/img_exit"
+        android:layout_alignParentBottom="true"
+        android:layout_alignParentRight="true"
+        android:layout_margin="16dp"/>
+
+
+    <LinearLayout
+        android:layout_width="wrap_content"
+        android:layout_alignParentBottom="true"
+        android:layout_height="44dp"
+        android:background="@drawable/shape_white"
+        android:layout_margin="16dp">
+        <ImageView
+            android:id="@+id/iv_switch"
+            android:layout_width="wrap_content"
+            android:padding="6dp"
+            android:layout_height="match_parent"
+            android:scaleType="center"
+            android:src="@drawable/img_switch"/>
+        <ImageView
+            android:id="@+id/iv_beauty"
+            android:layout_width="wrap_content"
+            android:padding="6dp"
+            android:layout_height="match_parent"
+            android:scaleType="center"
+            android:src="@drawable/select_beauty"/>
+
+        <ImageView
+            android:id="@+id/iv_audio"
+            android:layout_width="wrap_content"
+            android:padding="6dp"
+            android:layout_height="match_parent"
+            android:scaleType="center"
+            android:src="@drawable/select_audio"/>
+
+        <ImageView
+            android:id="@+id/iv_video"
+            android:layout_width="wrap_content"
+            android:padding="6dp"
+            android:layout_height="match_parent"
+            android:scaleType="center"
+            android:src="@drawable/select_video"/>
+
+        <ImageView
+            android:id="@+id/iv_mirro"
+            android:layout_width="wrap_content"
+            android:padding="6dp"
+            android:layout_height="match_parent"
+            android:scaleType="center"
+            android:src="@drawable/select_mirro"/>
+
+
+
+
+
+    </LinearLayout>
+
+
+
+</RelativeLayout>

BIN
app/src/main/res/mipmap-hdpi/ic_launcher.webp


BIN
app/src/main/res/mipmap-hdpi/ic_launcher_round.webp


BIN
app/src/main/res/mipmap-mdpi/ic_launcher.webp


BIN
app/src/main/res/mipmap-mdpi/ic_launcher_round.webp


BIN
app/src/main/res/mipmap-xhdpi/ic_launcher.webp


BIN
app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp


BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher.webp


BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp


BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp


BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp


+ 16 - 0
app/src/main/res/values-night/themes.xml

@@ -0,0 +1,16 @@
+<resources xmlns:tools="http://schemas.android.com/tools">
+    <!-- Base application theme. -->
+    <style name="Theme.LivePlayer" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
+        <!-- Primary brand color. -->
+        <item name="colorPrimary">@color/purple_200</item>
+        <item name="colorPrimaryVariant">@color/purple_700</item>
+        <item name="colorOnPrimary">@color/black</item>
+        <!-- Secondary brand color. -->
+        <item name="colorSecondary">@color/teal_200</item>
+        <item name="colorSecondaryVariant">@color/teal_200</item>
+        <item name="colorOnSecondary">@color/black</item>
+        <!-- Status bar color. -->
+        <item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
+        <!-- Customize your theme here. -->
+    </style>
+</resources>

+ 8 - 0
app/src/main/res/values/array.xml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string-array name="spinner">
+        <item>720P</item>
+        <item>540P </item>
+        <item>360P</item>
+    </string-array>
+</resources>

+ 10 - 0
app/src/main/res/values/colors.xml

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <color name="purple_200">#FFBB86FC</color>
+    <color name="purple_500">#FF6200EE</color>
+    <color name="purple_700">#FF3700B3</color>
+    <color name="teal_200">#FF03DAC5</color>
+    <color name="teal_700">#FF018786</color>
+    <color name="black">#FF000000</color>
+    <color name="white">#FFFFFFFF</color>
+</resources>

+ 7 - 0
app/src/main/res/values/strings.xml

@@ -0,0 +1,7 @@
+<resources>
+    <string name="app_name">LivePlayer</string>
+    <string name="push_desc">采用 WebRTC 底层架构,支持 RTMP/HLS/ \nHTTP-FLV</string>
+    <string name="pull_desc">低功耗直播播放器,支持软硬解切换,横竖切 \n换,低延迟等</string>
+    <string name="video_desc">支持首屏秒开、清晰度无缝切换、码率自适 \n应等多种特性</string>
+    <string name="live_desc">基于 anyRTC,强力打造直播互动连麦 \n各种互动场景</string>
+</resources>

+ 16 - 0
app/src/main/res/values/themes.xml

@@ -0,0 +1,16 @@
+<resources xmlns:tools="http://schemas.android.com/tools">
+    <!-- Base application theme. -->
+    <style name="Theme.LivePlayer" parent="Theme.AppCompat.Light.NoActionBar">
+        <!-- Primary brand color. -->
+        <item name="colorPrimary">@color/purple_500</item>
+        <item name="colorPrimaryVariant">@color/purple_700</item>
+        <item name="colorOnPrimary">@color/white</item>
+        <!-- Secondary brand color. -->
+        <item name="colorSecondary">@color/teal_200</item>
+        <item name="colorSecondaryVariant">@color/teal_700</item>
+        <item name="colorOnSecondary">@color/black</item>
+        <!-- Status bar color. -->
+        <item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
+        <!-- Customize your theme here. -->
+    </style>
+</resources>

+ 17 - 0
app/src/test/java/io/anyrtc/liveplayer/ExampleUnitTest.java

@@ -0,0 +1,17 @@
+package io.anyrtc.liveplayer;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
+ */
+public class ExampleUnitTest {
+    @Test
+    public void addition_isCorrect() {
+        assertEquals(4, 2 + 2);
+    }
+}

+ 19 - 0
build.gradle

@@ -0,0 +1,19 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+buildscript {
+    ext.kotlin_version = '1.0.0'
+    repositories {
+        google()
+        mavenCentral()
+    }
+    dependencies {
+        classpath "com.android.tools.build:gradle:7.0.1"
+        classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.30'
+        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+        // NOTE: Do not place your application dependencies here; they belong
+        // in the individual module build.gradle files
+    }
+}
+
+task clean(type: Delete) {
+    delete rootProject.buildDir
+}

+ 21 - 0
gradle.properties

@@ -0,0 +1,21 @@
+# 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.
+org.gradle.jvmargs=-Xmx2048m -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
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app"s APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Automatically convert third-party libraries to use AndroidX
+android.enableJetifier=true
+
+android.injected.testOnly = false

BIN
gradle/wrapper/gradle-wrapper.jar


+ 6 - 0
gradle/wrapper/gradle-wrapper.properties

@@ -0,0 +1,6 @@
+#Fri Sep 17 19:26:06 CST 2021
+distributionBase=GRADLE_USER_HOME
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip
+distributionPath=wrapper/dists
+zipStorePath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME

+ 185 - 0
gradlew

@@ -0,0 +1,185 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+##
+##  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='"-Xmx64m" "-Xms64m"'
+
+# 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 or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; 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=`expr $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"
+
+exec "$JAVACMD" "$@"

+ 89 - 0
gradlew.bat

@@ -0,0 +1,89 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem      https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@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 Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@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="-Xmx64m" "-Xms64m"
+
+@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 execute
+
+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 execute
+
+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
+
+: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 %*
+
+: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

+ 1 - 0
liveplayer/.gitignore

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

+ 52 - 0
liveplayer/build.gradle

@@ -0,0 +1,52 @@
+plugins {
+    id 'com.android.library'
+}
+
+android {
+    compileSdk 31
+
+    defaultConfig {
+        minSdk 19
+        targetSdk 31
+        versionCode 1
+        versionName "1.0"
+        consumerProguardFiles "consumer-rules.pro"
+        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+        externalNativeBuild {
+            cmake {
+                abiFilters 'arm64-v8a','armeabi-v7a'
+                cppFlags '-std=c++14 -DANDROID -fno-rtti'
+                arguments = ['-DANDROID_TOOLCHAIN=clang','-DANDROID_STL=c++_shared','-DCMAKE_BUILD_TYPE=Release']
+            }
+        }
+    }
+
+    buildTypes {
+        release {
+            minifyEnabled true
+        }
+        debug {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+        }
+    }
+    externalNativeBuild {
+        cmake {
+            path "src/main/cpp/CMakeLists.txt"
+            version "3.10.2"
+        }
+    }
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_8
+        targetCompatibility JavaVersion.VERSION_1_8
+    }
+    ndkVersion '20.0.5594570'
+
+}
+
+dependencies {
+
+    implementation 'androidx.appcompat:appcompat:1.3.1'
+    implementation 'com.google.android.material:material:1.4.0'
+    testImplementation 'junit:junit:4.+'
+}

+ 2 - 0
liveplayer/consumer-rules.pro

@@ -0,0 +1,2 @@
+-keep class io.anyrtc.** {*;}
+-keep class org.webrtc.** {*;}

+ 21 - 0
liveplayer/proguard-rules.pro

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

+ 26 - 0
liveplayer/src/androidTest/java/io/anyrtc/live/ExampleInstrumentedTest.java

@@ -0,0 +1,26 @@
+package io.anyrtc.live;
+
+import android.content.Context;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.*;
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+    @Test
+    public void useAppContext() {
+        // Context of the app under test.
+        Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        assertEquals("io.anyrtc.live.test", appContext.getPackageName());
+    }
+}

+ 33 - 0
liveplayer/src/main/AndroidManifest.xml

@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="io.anyrtc.live">
+    <uses-permission android:name="android.permission.BLUETOOTH" />
+    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
+    <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
+    <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
+
+    <uses-permission android:name="android.permission.INTERNET"></uses-permission>
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission>
+    <uses-permission android:name="android.permission.CAMERA"></uses-permission>
+    <uses-permission android:name="android.permission.RECORD_AUDIO"></uses-permission>
+    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"></uses-permission>
+    <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
+    <uses-permission android:name="android.permission.BLUETOOTH"></uses-permission>
+    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
+    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"></uses-permission>
+    <uses-permission android:name="android.permission.FLASHLIGHT"></uses-permission>
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"></uses-permission>
+    <application
+        android:networkSecurityConfig="@xml/network_security_config">
+
+        <service android:name=".ArScreenService"
+            android:foregroundServiceType="mediaProjection"></service>
+
+        <activity android:name=".internal.ArScreenCapture$ArScreenCaptureAssistantActivity"
+            android:theme="@android:style/Theme.Translucent.NoTitleBar.Fullscreen"
+            ></activity>
+
+    </application>
+
+</manifest>

+ 138 - 0
liveplayer/src/main/cpp/CMakeLists.txt

@@ -0,0 +1,138 @@
+# For more information about using CMake with Android Studio, read the
+# documentation: https://d.android.com/studio/projects/add-native-code.html
+
+# Sets the minimum version of CMake required to build the native library.
+cmake_minimum_required(VERSION 3.10.2)
+project("anyLive")
+enable_language(ASM)
+set(CMAKE_ASM_COMPILER ${CMAKE_C_COMPILER})
+set(STATIC_LIB_DIR ${CMAKE_SOURCE_DIR}/lib/)
+set(ROOT_DIR ./../../../../../)
+set(ARLIVE_DIR ./../../../../../ArLiveLite)#publish
+#set(ARLIVE_DIR ./ArLiveLite)#debug
+set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--gc-sections -Wl,--exclude-libs,
+${STATIC_LIB_DIR}/ffmpeg/${ANDROID_ABI}/libswscale.so,
+${STATIC_LIB_DIR}/ffmpeg/${ANDROID_ABI}/libavformat.so,
+${STATIC_LIB_DIR}/ffmpeg/${ANDROID_ABI}/libavcodec.so,
+${STATIC_LIB_DIR}/ffmpeg/${ANDROID_ABI}/libavfilter.so,
+${STATIC_LIB_DIR}/ffmpeg/${ANDROID_ABI}/libavutil.so,
+${STATIC_LIB_DIR}/ffmpeg/${ANDROID_ABI}/libswresample.so,
+${STATIC_LIB_DIR}/ffmpeg/${ANDROID_ABI}/libpostproc.so,
+${STATIC_LIB_DIR}/webrtc/${ANDROID_ABI}/libwebrtc.a")
+
+set(NATIVE_LIB "anyLive")
+
+add_library(avutil SHARED IMPORTED)
+set_target_properties(avutil PROPERTIES IMPORTED_LOCATION ${STATIC_LIB_DIR}/ffmpeg/${ANDROID_ABI}/libavutil.so)
+
+add_library(avformat SHARED IMPORTED)
+set_target_properties(avformat PROPERTIES IMPORTED_LOCATION ${STATIC_LIB_DIR}/ffmpeg/${ANDROID_ABI}/libavformat.so)
+
+add_library(avcodec SHARED IMPORTED)
+set_target_properties(avcodec PROPERTIES IMPORTED_LOCATION ${STATIC_LIB_DIR}/ffmpeg/${ANDROID_ABI}/libavcodec.so)
+
+add_library(avfilter SHARED IMPORTED)
+set_target_properties(avfilter PROPERTIES IMPORTED_LOCATION ${STATIC_LIB_DIR}/ffmpeg/${ANDROID_ABI}/libavfilter.so)
+
+add_library(swresample SHARED IMPORTED)
+set_target_properties(swresample PROPERTIES IMPORTED_LOCATION ${STATIC_LIB_DIR}/ffmpeg/${ANDROID_ABI}/libswresample.so)
+
+add_library(swscale SHARED IMPORTED)
+set_target_properties(swscale PROPERTIES IMPORTED_LOCATION ${STATIC_LIB_DIR}/ffmpeg/${ANDROID_ABI}/libswscale.so)
+
+add_library(postproc SHARED IMPORTED)
+set_target_properties(postproc PROPERTIES IMPORTED_LOCATION ${STATIC_LIB_DIR}/ffmpeg/${ANDROID_ABI}/libpostproc.so)
+
+add_library(webrtc STATIC IMPORTED)
+set_target_properties(webrtc PROPERTIES IMPORTED_LOCATION ${STATIC_LIB_DIR}/webrtc/${ANDROID_ABI}/libwebrtc.a)
+
+include(${ROOT_DIR}/third_party/faac-1.28/CMakeLists.txt)
+add_library(
+        ${NATIVE_LIB}
+        SHARED
+        ${CMAKE_SOURCE_DIR}/jni/LiveEngine.cpp
+        ${CMAKE_SOURCE_DIR}/jni/StaticThreads.cpp
+        ${CMAKE_SOURCE_DIR}/jni/android/AndroidContext.cpp
+        ${CMAKE_SOURCE_DIR}/jni/android/VideoCameraCapturer.cpp
+        ${CMAKE_SOURCE_DIR}/jni/liveEngine/ArLivePlayEvent.cpp
+        ${CMAKE_SOURCE_DIR}/jni/liveEngine/ArLivePushEvent.cpp
+        ${CMAKE_SOURCE_DIR}/jni/liveEngine/AndroidDeviceManager.cpp
+
+       ${ARLIVE_DIR}/AndroidRenderer.cpp
+       ${ARLIVE_DIR}/ArLive2Engine.cpp
+       ${ARLIVE_DIR}/PlatformImpl.cpp
+       ${ARLIVE_DIR}/ArLive2Player.cpp
+       ${ARLIVE_DIR}/ArLive2Pusher.cpp
+       ${ARLIVE_DIR}/codec/aacencode.cc
+       ${ARLIVE_DIR}/codec/AvCodec.cc
+       ${ARLIVE_DIR}/MgrRender.cpp
+       ${ARLIVE_DIR}/PlayBuffer.cpp
+       ${ARLIVE_DIR}/player/ARFFPlayer.cpp
+       ${ARLIVE_DIR}/player/FFBuffer.cpp
+       ${ARLIVE_DIR}/player/sonic.c
+       ${ARLIVE_DIR}/pusher/ARFFPusher.cpp
+       ${ARLIVE_DIR}/pusher/ArFFWriter.cpp
+       ${ARLIVE_DIR}/ArNetClient.cpp
+       ${ARLIVE_DIR}/ArNetTcpClient.cpp
+       ${ARLIVE_DIR}/RtcTick.cpp
+       ${ARLIVE_DIR}/H264SeiPack.cpp
+       ${CMAKE_SOURCE_DIR}/jni/util/ClassreferenceHolder.cc
+
+)
+
+find_library( # Sets the name of the path variable.
+        log-lib
+
+        # Specifies the name of the NDK library that
+        # you want CMake to locate.
+        log)
+
+target_link_libraries(
+        ${NATIVE_LIB}
+        faac
+        -Wl,--start-group
+        swscale
+        avformat
+        avcodec
+        avfilter
+        swresample
+        postproc
+        avutil
+        -Wl,--end-group
+        -Wl,--whole-archive
+        webrtc
+        -Wl,--no-whole-archive
+        OpenSLES
+        ${log-lib}
+)
+
+
+
+include_directories(${CMAKE_SOURCE_DIR})
+include_directories(${CMAKE_SOURCE_DIR}/jni/)
+include_directories(${CMAKE_SOURCE_DIR}/jni/util/)
+include_directories(${CMAKE_SOURCE_DIR}/jni/android/)
+include_directories(${CMAKE_SOURCE_DIR}/webrtc)
+include_directories(${CMAKE_SOURCE_DIR}/webrtc/sdk/)
+include_directories(${CMAKE_SOURCE_DIR}/webrtc/sdk/android/src/jni/)
+include_directories(${CMAKE_SOURCE_DIR}/webrtc/third_party/abseil-cpp/)
+include_directories(${CMAKE_SOURCE_DIR}/lib/ffmpeg/include/)
+include_directories(${ARLIVE_DIR})
+include_directories(${ARLIVE_DIR}/include/)
+include_directories(${ARLIVE_DIR}/codec/)
+include_directories(${ARLIVE_DIR}/player/)
+include_directories(${ARLIVE_DIR}/pusher/)
+include_directories(${ROOT_DIR}/VideoRender/)
+include_directories(${ROOT_DIR}/third_party/)
+include_directories(${ROOT_DIR}/third_party/libyuv/include/)
+include_directories(${ROOT_DIR}/third_party/rapidjson/)
+
+
+
+
+add_definitions("-DWEBRTC_POSIX")
+add_definitions("-DWEBRTC_ANDROID")
+add_definitions("-DWEBRTC_USE_H264")
+add_definitions("-DNDEBUG")
+add_definitions("-DBUILDFLAG_INTERNAL_EXPENSIVE_DCHECKS_ARE_ON")
+add_definitions("-DRW_LOCK")

+ 866 - 0
liveplayer/src/main/cpp/jni/LiveEngine.cpp

@@ -0,0 +1,866 @@
+#include <jni.h>
+#include <string>
+#include <sdk/android/src/jni/jni_helpers.h>
+#include <rtc_base/ssl_adapter.h>
+#include <sdk/android/native_api/video/wrapper.h>
+#include <modules/utility/include/jvm_android.h>
+#include <sdk/android/native_api/base/init.h>
+#include <memory>
+#include <utility>
+#include <map>
+#include "ArLive2Engine.h"
+#include "IArLivePusher.hpp"
+#include <android/AndroidContext.h>
+#include <android/native_api/jni/class_loader.h>
+#include "liveEngine/ArLivePushEvent.h"
+#include "liveEngine/ArLivePlayEvent.h"
+#include "media/engine/webrtc_media_engine.h"
+#include "libyuv.h"
+#include "util/ClassreferenceHolder.h"
+#include "liveEngine/AndroidDeviceManager.h"
+extern "C" {
+using namespace arlive;
+using namespace anyrtc;
+bool webrtcLoaded = false;
+jclass NativeInstance;
+
+void initWebRTC(JNIEnv *env) {
+    if (webrtcLoaded) {
+        return;
+    }
+    JavaVM *vm;
+    env->GetJavaVM(&vm);
+    webrtc::InitAndroid(vm);
+    webrtc::JVM::Initialize(vm);
+    webrtcLoaded = true;
+    NativeInstance = static_cast<jclass>(env->NewGlobalRef(
+            env->FindClass("io/anyrtc/live/internal/NativeInstance")));
+    webrtc_loader::LoadGlobalClassReferenceHolder();
+}
+
+
+struct InstanceHolder {
+    std::shared_ptr<PlatformContext> _platformContext;
+    IArLive2Engine *arLiveEngine;
+};
+
+jlong getInstanceHolderId(JNIEnv *env, jobject obj) {
+    return env->GetLongField(obj, env->GetFieldID(NativeInstance, "nativePtr", "J"));
+}
+
+InstanceHolder *getInstanceHolder(JNIEnv *env, jobject obj) {
+    return reinterpret_cast<InstanceHolder *>(getInstanceHolderId(env, obj));
+}
+
+
+}
+
+extern "C" {
+JNIEXPORT jlong JNICALL
+Java_io_anyrtc_live_internal_NativeInstance_makeNativeInstance(JNIEnv *env, jobject obj, jobject instance) {
+    initWebRTC(env);
+    auto *holder = new InstanceHolder();
+    holder->arLiveEngine = V2_CALL::createArLive2Engine();
+    std::shared_ptr<PlatformContext> platformContext;
+    platformContext = std::make_shared<AndroidContext>(env,obj, false);
+    holder->_platformContext = platformContext;
+    AndroidDeviceManager::Inst().setPlatformContext(platformContext);
+    holder->arLiveEngine->initialize(NULL);
+    return reinterpret_cast<jlong>(holder);
+}
+
+JNIEXPORT jint JNICALL
+Java_io_anyrtc_live_internal_NativeInstance_nativeSetAppInBackground(JNIEnv *env, jobject obj,
+                                                                     jboolean background) {
+    InstanceHolder *instance = getInstanceHolder(env, obj);
+    if (instance->arLiveEngine == nullptr) {
+        return (jint) -1;
+    }
+    instance->arLiveEngine->setAppInBackground(background);
+    return (jint) 0;
+}
+
+
+
+
+JNIEXPORT jlong JNICALL
+Java_io_anyrtc_live_internal_NativeInstance_nativeCreatePushKit(JNIEnv *env, jobject obj) {
+    InstanceHolder *instance = getInstanceHolder(env, obj);
+    if (instance->arLiveEngine == nullptr){
+        return (jint)-1;
+    }
+    IArLivePusher *arLivePushKit = instance->arLiveEngine->createArLivePusher(NULL);
+    std::unique_ptr<webrtc::VideoEncoderFactory> video_encoder_factory = AndroidDeviceManager::Inst().makeVideoEncoderFactory();
+    arLivePushKit->setExVideoEncoderFactory(video_encoder_factory.release());
+    return reinterpret_cast<jlong>(arLivePushKit);
+}
+
+JNIEXPORT void JNICALL
+Java_io_anyrtc_live_internal_NativeInstance_nativeSetPushObserver(JNIEnv *env, jobject obj,jobject observer,jlong nativePtr) {
+    IArLivePusher* arLivePushKit = reinterpret_cast<IArLivePusher *>(nativePtr);
+    if (arLivePushKit != NULL){
+        LivePushEvent *pushEvent = new LivePushEvent(observer);
+        arLivePushKit->setObserver(pushEvent);
+    }
+}
+
+
+JNIEXPORT jint JNICALL
+Java_io_anyrtc_live_internal_NativeInstance_nativeStartPush(JNIEnv *env, jobject obj,jlong nativePtr,jstring pushUrl) {
+    IArLivePusher* arLivePushKit = reinterpret_cast<IArLivePusher *>(nativePtr);
+    if (arLivePushKit != NULL){
+        std::string strPushUrl = webrtc::JavaToStdString(env, pushUrl);
+        int result =arLivePushKit->startPush(strPushUrl.c_str());
+        return (jint)result;
+    }
+    return (jint)-1;
+}
+
+JNIEXPORT jint JNICALL
+Java_io_anyrtc_live_internal_NativeInstance_nativeStopPush(JNIEnv *env, jobject obj,jlong nativePtr) {
+    InstanceHolder *instance = getInstanceHolder(env, obj);
+    IArLivePusher* arLivePushKit = reinterpret_cast<IArLivePusher *>(nativePtr);
+    
+    if (arLivePushKit != NULL&&instance->arLiveEngine!=NULL){
+        int result =arLivePushKit->stopPush();
+        return (jint)result;
+    }
+    return (jint)-1;
+}
+
+JNIEXPORT void JNICALL
+Java_io_anyrtc_live_internal_NativeInstance_nativeReleasePusher(JNIEnv *env, jobject obj,jlong nativePtr) {
+    InstanceHolder *instance = getInstanceHolder(env, obj);
+    IArLivePusher* arLivePushKit = reinterpret_cast<IArLivePusher *>(nativePtr);
+    if (arLivePushKit != NULL&&instance->arLiveEngine!=NULL){
+        instance->arLiveEngine->releaseArLivePusher(arLivePushKit);
+    }
+}
+
+
+
+
+JNIEXPORT jint JNICALL
+Java_io_anyrtc_live_internal_NativeInstance_nativeIsPushing(JNIEnv *env, jobject obj,jlong nativePtr) {
+    IArLivePusher* arLivePushKit = reinterpret_cast<IArLivePusher *>(nativePtr);
+    if (arLivePushKit!=NULL){
+        bool result = arLivePushKit->isPushing();
+        return (jint)result;
+    }
+    return (jint)-1;
+  
+}
+
+
+
+
+
+JNIEXPORT void JNICALL
+Java_io_anyrtc_live_internal_NativeInstance_nativeRelease(JNIEnv *env, jobject obj) {
+    InstanceHolder *instance = getInstanceHolder(env, obj);
+    if (instance == NULL){
+        return;
+    }
+    if (instance->arLiveEngine != NULL){
+        instance->arLiveEngine->release();
+    }
+    delete instance;
+}
+
+
+JNIEXPORT jint JNICALL
+Java_io_anyrtc_live_internal_NativeInstance_nativeSetRenderView(JNIEnv *env, jobject obj, jlong nativePtr,jobject localSink) {
+    jint result = -1;
+    IArLivePusher* arLivePushKit = reinterpret_cast<IArLivePusher *>(nativePtr);
+    if (arLivePushKit!= NULL){
+        result = arLivePushKit->setRenderView(reinterpret_cast<view_t *>(localSink));
+    }
+    return (jint)result;
+}
+
+JNIEXPORT jint JNICALL
+Java_io_anyrtc_live_internal_NativeInstance_nativeSetPushRenderRotation(JNIEnv *env, jobject thiz,jlong nativeHandle,jint rotation
+) {
+    jint result = -1;
+    IArLivePusher* arLivePushKit = reinterpret_cast<IArLivePusher *>(nativeHandle);
+    if (arLivePushKit != NULL){
+        result = arLivePushKit->setRenderRotation(static_cast<ArLiveRotation>(rotation));
+    }
+    return (jint)result;
+}
+
+
+JNIEXPORT jint JNICALL
+Java_io_anyrtc_live_internal_NativeInstance_nativeStartCamera(JNIEnv *env, jobject obj,jlong nativePtr,jboolean isFront) {
+    jint result = -1;
+    IArLivePusher* arLivePushKit = reinterpret_cast<IArLivePusher *>(nativePtr);
+    if (arLivePushKit!= NULL){
+        result = arLivePushKit->startCamera(isFront);
+    }
+    return (jint)result;
+}
+
+JNIEXPORT jint JNICALL
+Java_io_anyrtc_live_internal_NativeInstance_nativeStopCamera(JNIEnv *env, jobject obj,jlong nativePtr) {
+    jint result = -1;
+    IArLivePusher* arLivePushKit = reinterpret_cast<IArLivePusher *>(nativePtr);
+    if (arLivePushKit!= NULL){
+        result = arLivePushKit->stopCamera();
+    }
+    return (jint)result;
+}
+
+JNIEXPORT jint JNICALL
+Java_io_anyrtc_live_internal_NativeInstance_nativeSetEncoderMirror(JNIEnv *env, jobject obj,jlong nativePtr,jboolean mirror) {
+    jint result = -1;
+    IArLivePusher* arLivePushKit = reinterpret_cast<IArLivePusher *>(nativePtr);
+    if (arLivePushKit!= NULL){
+        result = arLivePushKit->setEncoderMirror(mirror);
+    }
+    return (jint)result;
+}
+
+JNIEXPORT jint JNICALL
+Java_io_anyrtc_live_internal_NativeInstance_nativeStartMicrophone(JNIEnv *env, jobject obj,jlong nativePtr) {
+    jint result = -1;
+    IArLivePusher* arLivePushKit = reinterpret_cast<IArLivePusher *>(nativePtr);
+    if (arLivePushKit!= NULL){
+        result = arLivePushKit->startMicrophone();
+    }
+    return (jint)result;
+}
+
+JNIEXPORT jint JNICALL
+Java_io_anyrtc_live_internal_NativeInstance_nativeStopMicrophone(JNIEnv *env, jobject obj,jlong nativePtr) {
+    jint result = -1;
+    IArLivePusher* arLivePushKit = reinterpret_cast<IArLivePusher *>(nativePtr);
+    if (arLivePushKit!= NULL){
+        result = arLivePushKit->stopMicrophone();
+    }
+    return (jint)result;
+}
+
+JNIEXPORT jint JNICALL
+Java_io_anyrtc_live_internal_NativeInstance_nativeSetBeautyEffect(JNIEnv *env, jobject obj,jboolean enable) {
+    return (jint)AndroidDeviceManager::Inst().setBeautyEffect(enable);
+}
+
+JNIEXPORT jint JNICALL
+Java_io_anyrtc_live_internal_NativeInstance_nativeSetWhitenessLevel(JNIEnv *env, jobject obj,
+                                                                    jfloat level) {
+    return (jint)AndroidDeviceManager::Inst().setWhitenessLevel(level);
+}
+JNIEXPORT jint JNICALL
+Java_io_anyrtc_live_internal_NativeInstance_nativeSetBeautyLevel(JNIEnv *env, jobject obj,
+                                                                 jfloat level) {
+    return (jint)AndroidDeviceManager::Inst().setBeautyLevel(level);
+}
+JNIEXPORT jint JNICALL
+Java_io_anyrtc_live_internal_NativeInstance_nativeSetToneLevel(JNIEnv *env, jobject obj,
+                                                               jfloat level) {
+    return (jint)AndroidDeviceManager::Inst().setToneLevel(level);
+}
+
+
+JNIEXPORT jint JNICALL
+Java_io_anyrtc_live_internal_NativeInstance_nativeStartVirtualCamera(JNIEnv *env, jobject thiz,
+                                                                     jlong nativePtr,jint type,
+                                                                     jbyteArray bitmap, jint width,
+                                                                     jint height) {
+    jint result = -1;
+    IArLivePusher* arLivePushKit = reinterpret_cast<IArLivePusher *>(nativePtr);
+    if (arLivePushKit!= NULL){
+        jbyte* bufferPtr=(env)->GetByteArrayElements(bitmap, NULL);
+        ArLiveImage *image = new ArLiveImage();
+        image->imageType = static_cast<ArLiveImageType>(type);
+        image->imageWidth = width;
+        image->imageHeight = height;
+        image->imageSrc = reinterpret_cast<const char *>(bufferPtr);
+        result = arLivePushKit->startVirtualCamera(image);
+        env->ReleaseByteArrayElements(bitmap, bufferPtr, 0);
+    }
+    return (jint)result;
+}
+JNIEXPORT jint JNICALL
+Java_io_anyrtc_live_internal_NativeInstance_nativeStopVirtualCamera(JNIEnv *env, jobject thiz, jlong nativePtr) {
+    jint result = -1;
+    IArLivePusher* arLivePushKit = reinterpret_cast<IArLivePusher *>(nativePtr);
+    if (arLivePushKit!= NULL){
+        result = arLivePushKit->stopVirtualCamera();
+    }
+    return (jint)result;
+}
+
+JNIEXPORT jint JNICALL
+Java_io_anyrtc_live_internal_NativeInstance_nativePausePusherAudio(JNIEnv *env, jobject obj,jlong nativePtr) {
+    jint result = -1;
+    IArLivePusher* arLivePushKit = reinterpret_cast<IArLivePusher *>(nativePtr);
+    if (arLivePushKit!= NULL){
+        result = arLivePushKit->pauseAudio();
+    }
+    return (jint)result;
+}
+JNIEXPORT jint JNICALL
+Java_io_anyrtc_live_internal_NativeInstance_nativeResumePusherAudio(JNIEnv *env, jobject obj,jlong nativePtr) {
+    jint result = -1;
+    IArLivePusher* arLivePushKit = reinterpret_cast<IArLivePusher *>(nativePtr);
+    if (arLivePushKit!= NULL){
+        result = arLivePushKit->resumeAudio();
+    }
+    return (jint)result;
+}
+JNIEXPORT jint JNICALL
+Java_io_anyrtc_live_internal_NativeInstance_nativePausePusherVideo(JNIEnv *env, jobject obj,jlong nativePtr) {
+    jint result = -1;
+    IArLivePusher* arLivePushKit = reinterpret_cast<IArLivePusher *>(nativePtr);
+    if (arLivePushKit!= NULL){
+        result = arLivePushKit->pauseVideo();
+    }
+    return (jint)result;
+}
+JNIEXPORT jint JNICALL
+Java_io_anyrtc_live_internal_NativeInstance_nativeResumePusherVideo(JNIEnv *env, jobject obj,jlong nativePtr) {
+    jint result = -1;
+    IArLivePusher* arLivePushKit = reinterpret_cast<IArLivePusher *>(nativePtr);
+    if (arLivePushKit!= NULL){
+        result = arLivePushKit->resumeVideo();
+    }
+    return (jint)result;
+}
+
+
+JNIEXPORT void JNICALL
+Java_io_anyrtc_live_internal_NativeInstance_switchCamera(JNIEnv *env, jobject obj,jboolean isFront) {
+        AndroidDeviceManager::Inst().switchCamera(isFront);
+}
+
+
+
+JNIEXPORT jint JNICALL
+Java_io_anyrtc_live_internal_NativeInstance_nativeSetVideoQuality(JNIEnv *env, jobject obj,jlong nativePtr,jint videoResolution,jint videoResolutionMode,jint videoFps,jint videoBitrate,jint minVideoBitrate,jint scaleMode) {
+    jint result = -1;
+    IArLivePusher* arLivePushKit = reinterpret_cast<IArLivePusher *>(nativePtr);
+    if (arLivePushKit!= NULL){
+        AR::ArLiveVideoEncoderParam param = anyrtc::ArLiveVideoEncoderParam(ArLiveVideoResolution640x480);
+        param.videoResolution = static_cast<ArLiveVideoResolution>(videoResolution);
+        param.videoResolutionMode = static_cast<ArLiveVideoResolutionMode>(videoResolutionMode);
+        param.videoBitrate = videoBitrate;
+        param.videoFps  = videoFps;
+        param.minVideoBitrate = minVideoBitrate;
+        param.videoScaleMode= static_cast<ArLiveVideoScaleMode>(scaleMode);
+        result = arLivePushKit->setVideoQuality(param);
+    }
+    return (jint)result;
+}
+
+JNIEXPORT jint JNICALL
+Java_io_anyrtc_live_internal_NativeInstance_nativeSetAudioQuality(JNIEnv *env, jobject obj,jlong nativePtr,jint mode) {
+    jint result = -1;
+    IArLivePusher* arLivePushKit = reinterpret_cast<IArLivePusher *>(nativePtr);
+    if (arLivePushKit!= NULL){
+        result = arLivePushKit->setAudioQuality(static_cast<ArLiveAudioQuality>(mode));
+    }
+    return (jint)result;
+}
+
+JNIEXPORT jint JNICALL
+Java_io_anyrtc_live_internal_NativeInstance_nativePusherEnableVolumeEvaluation(JNIEnv *env,
+                                                                               jobject thiz,jlong nativePtr,
+                                                                               jint interval_ms) {
+    IArLivePusher* arLivePushKit = reinterpret_cast<IArLivePusher *>(nativePtr);
+    jint result = -1;
+    if (arLivePushKit!= NULL){
+        result = arLivePushKit->enableVolumeEvaluation(interval_ms);
+    }
+    return (jint)result;
+}
+
+JNIEXPORT jint JNICALL
+Java_io_anyrtc_live_internal_NativeInstance_nativeSendSeiMessage(JNIEnv *env, jobject thiz,jlong nativePtr,
+                                                                 jint var1, jbyteArray var2) {
+    IArLivePusher* arLivePushKit = reinterpret_cast<IArLivePusher *>(nativePtr);
+    jint result = -1;
+    if (arLivePushKit!= NULL){
+        jbyte* bufferPtr=(env)->GetByteArrayElements(var2, NULL);
+        jint size = env->GetArrayLength(var2);
+        result = arLivePushKit->sendSeiMessage(var1, reinterpret_cast<const uint8_t *>(bufferPtr), size);
+        env->ReleaseByteArrayElements(var2, bufferPtr, 0);
+    }
+    return (jint)result;
+
+}
+
+JNIEXPORT jint JNICALL
+Java_io_anyrtc_live_internal_NativeInstance_nativeEnableCustomVideoCapture(JNIEnv *env,
+                                                                           jobject thiz,
+                                                                           jlong nativePtr,
+                                                                           jboolean var1) {
+    IArLivePusher* arLivePushKit = reinterpret_cast<IArLivePusher *>(nativePtr);
+    jint result = -1;
+    if (arLivePushKit!= NULL){
+        result = arLivePushKit->enableCustomVideoCapture(var1);
+    }
+    return (jint)result;
+}
+
+JNIEXPORT jint JNICALL
+Java_io_anyrtc_live_internal_NativeInstance_nativeSendCustomVideoFrame(JNIEnv *env, jobject thiz,
+                                                                       jlong nativePtr,
+                                                                       jint pixel_format,
+                                                                       jint buffer_type,
+                                                                       jbyteArray data,
+                                                                       jobject buffer, jint width,
+                                                                       jint height, jint rotation,jint stride) {
+    IArLivePusher* arLivePushKit = reinterpret_cast<IArLivePusher *>(nativePtr);
+    jint result = -1;
+    if (arLivePushKit!= NULL){
+        ArLiveVideoFrame *frame = new ArLiveVideoFrame();
+        frame->pixelFormat = static_cast<ArLivePixelFormat>(pixel_format);
+        frame->bufferType = static_cast<ArLiveBufferType>(buffer_type);
+        frame->width = width;
+        frame->height = height;
+        frame->rotation = static_cast<ArLiveRotation>(rotation);
+        jbyte* arrayData=(env)->GetByteArrayElements(data, NULL);
+        frame->data = reinterpret_cast<char *>(arrayData);
+        frame->stride = stride;
+        result = arLivePushKit->sendCustomVideoFrame(frame);
+        env->ReleaseByteArrayElements(data, arrayData, 0);
+    }
+    return (jint)result;
+}
+
+
+JNIEXPORT jint JNICALL
+Java_io_anyrtc_live_internal_NativeInstance_nativeEnableCustomAudioCapture(JNIEnv *env,
+                                                                           jobject thiz,jlong nativePtr,
+                                                                           jboolean enable) {
+    IArLivePusher* arLivePushKit = reinterpret_cast<IArLivePusher *>(nativePtr);
+    jint result  = -1;
+    if (arLivePushKit!= NULL){
+        result = arLivePushKit->enableCustomAudioCapture(enable);
+    }
+    return (jint)result;
+}
+
+JNIEXPORT jint JNICALL
+Java_io_anyrtc_live_internal_NativeInstance_nativeSendCustomAudioFrame(JNIEnv *env, jobject thiz,
+                                                                       jlong nativePtr,
+                                                                       jint channel,
+                                                                       jint sample_rate,
+                                                                       jbyteArray data) {
+    IArLivePusher* arLivePushKit = reinterpret_cast<IArLivePusher *>(nativePtr);
+    jint result  = -1;
+    if (arLivePushKit!= NULL){
+        jbyte* bufferPtr=(env)->GetByteArrayElements(data, NULL);
+        jint size = env->GetArrayLength(data);
+        ArLiveAudioFrame *audioFrame = new ArLiveAudioFrame();
+        audioFrame->data = reinterpret_cast<char *>(bufferPtr);
+        audioFrame->channel = channel;
+        audioFrame->sampleRate = sample_rate;
+        audioFrame->length = size;
+        result = arLivePushKit->sendCustomAudioFrame(audioFrame);
+        env->ReleaseByteArrayElements(data, bufferPtr, 0);
+    }
+    return (jint)result;
+}
+
+
+
+
+JNIEXPORT void JNICALL
+Java_io_anyrtc_live_internal_NativeInstance_startScreenCapture(JNIEnv *env, jobject obj,jlong nativePtr) {
+    IArLivePusher* arLivePushKit = reinterpret_cast<IArLivePusher *>(nativePtr);
+    if (arLivePushKit!=NULL){
+        arLivePushKit->startScreenCapture();
+    }
+}
+
+JNIEXPORT void JNICALL
+Java_io_anyrtc_live_internal_NativeInstance_stopScreenCapture(JNIEnv *env, jobject obj,jlong nativePtr) {
+    IArLivePusher* arLivePushKit = reinterpret_cast<IArLivePusher *>(nativePtr);
+    if (arLivePushKit!=NULL){
+        arLivePushKit->stopScreenCapture();
+    }
+}
+
+
+
+
+
+
+}
+
+
+extern "C" {
+
+JNIEXPORT jint JNICALL
+Java_io_anyrtc_live_internal_NativeInstance_nativeStartPlay(JNIEnv *env, jobject thiz,
+                                                            jlong native_ptr, jstring url) {
+
+    int result = -1;
+    IArLivePlayer* arLivePlayKit = reinterpret_cast<IArLivePlayer *>(native_ptr);
+    if (arLivePlayKit !=NULL){
+        std::string strUrl = webrtc::JavaToStdString(env, url);
+        result = arLivePlayKit->startPlay(strUrl.c_str());
+    }
+    return (jint)result;
+}
+
+
+JNIEXPORT jint JNICALL
+Java_io_anyrtc_live_internal_NativeInstance_nativeStopPlay(JNIEnv *env, jobject thiz,
+                                                            jlong nativeHandle) {
+    jint result = -1;
+    IArLivePlayer* arLivePlayKit = reinterpret_cast<IArLivePlayer *>(nativeHandle);
+    if (arLivePlayKit != NULL){
+        result = arLivePlayKit->stopPlay();
+    }
+    return (jint)result;
+}
+
+JNIEXPORT jint JNICALL
+Java_io_anyrtc_live_internal_NativeInstance_nativeIsPlaying(JNIEnv *env, jobject thiz,jlong nativeHandle
+                                                          ) {
+    jint result = -1;
+    IArLivePlayer* arLivePlayKit = reinterpret_cast<IArLivePlayer *>(nativeHandle);
+    if (arLivePlayKit != NULL){
+        result = arLivePlayKit->isPlaying();
+    }
+    return (jint)result;
+}
+JNIEXPORT jint JNICALL
+Java_io_anyrtc_live_internal_NativeInstance_nativeSetRenderRotation(JNIEnv *env, jobject thiz,jlong nativeHandle,jint rotation
+) {
+    jint result = -1;
+    IArLivePlayer* arLivePlayKit = reinterpret_cast<IArLivePlayer *>(nativeHandle);
+    if (arLivePlayKit != NULL){
+        result = arLivePlayKit->setRenderRotation(static_cast<ArLiveRotation>(rotation));
+    }
+    return (jint)result;
+}
+
+
+JNIEXPORT jint JNICALL
+Java_io_anyrtc_live_internal_NativeInstance_nativePauseAudio(JNIEnv *env, jobject thiz,jlong nativeHandle
+) {
+    jint result = -1;
+    IArLivePlayer* arLivePlayKit = reinterpret_cast<IArLivePlayer *>(nativeHandle);
+    if (arLivePlayKit != NULL){
+        result = arLivePlayKit->pauseAudio();
+    }
+    return (jint)result;
+}
+
+JNIEXPORT jint JNICALL
+Java_io_anyrtc_live_internal_NativeInstance_nativeResumeAudio(JNIEnv *env, jobject thiz,jlong nativeHandle
+) {
+    jint result = -1;
+    IArLivePlayer* arLivePlayKit = reinterpret_cast<IArLivePlayer *>(nativeHandle);
+    if (arLivePlayKit != NULL){
+        result = arLivePlayKit->resumeAudio();
+    }
+    return (jint)result;
+}
+
+JNIEXPORT jint JNICALL
+Java_io_anyrtc_live_internal_NativeInstance_nativeResumeVideo(JNIEnv *env, jobject thiz,jlong nativeHandle
+) {
+    jint result = -1;
+    IArLivePlayer* arLivePlayKit = reinterpret_cast<IArLivePlayer *>(nativeHandle);
+    if (arLivePlayKit != NULL){
+        result = arLivePlayKit->resumeVideo();
+    }
+    return (jint)result;
+}
+
+
+
+JNIEXPORT jint JNICALL
+Java_io_anyrtc_live_internal_NativeInstance_nativePauseVideo(JNIEnv *env, jobject thiz,jlong nativeHandle
+) {
+    jint result = -1;
+    IArLivePlayer* arLivePlayKit = reinterpret_cast<IArLivePlayer *>(nativeHandle);
+    if (arLivePlayKit != NULL){
+        result = arLivePlayKit->pauseVideo();
+    }
+    return (jint)result;
+}
+
+
+JNIEXPORT jint JNICALL
+Java_io_anyrtc_live_internal_NativeInstance_nativeSetPlayoutVolume(JNIEnv *env, jobject thiz,jlong nativeHandle,jint vom
+) {
+    jint result = -1;
+    IArLivePlayer* arLivePlayKit = reinterpret_cast<IArLivePlayer *>(nativeHandle);
+    if (arLivePlayKit != NULL){
+        result = arLivePlayKit->setPlayoutVolume(vom);
+    }
+    return (jint)result;
+}
+
+JNIEXPORT jint JNICALL
+Java_io_anyrtc_live_internal_NativeInstance_nativeSetCacheParams(JNIEnv *env, jobject thiz,jlong nativeHandle,jfloat minTime,jfloat maxTime
+) {
+    jint result = -1;
+    IArLivePlayer* arLivePlayKit = reinterpret_cast<IArLivePlayer *>(nativeHandle);
+    if (arLivePlayKit != NULL){
+        result = arLivePlayKit->setCacheParams(minTime,maxTime);
+    }
+    return (jint)result;
+}
+
+JNIEXPORT jint JNICALL
+Java_io_anyrtc_live_internal_NativeInstance_nativeEnableVolumeEvaluation(JNIEnv *env, jobject thiz,jlong nativeHandle,jint intervalMs
+) {
+    jint result = -1;
+    IArLivePlayer* arLivePlayKit = reinterpret_cast<IArLivePlayer *>(nativeHandle);
+    if (arLivePlayKit != NULL){
+        result = arLivePlayKit->enableVolumeEvaluation(intervalMs);
+    }
+    return (jint)result;
+}
+
+
+JNIEXPORT jint JNICALL
+Java_io_anyrtc_live_internal_NativeInstance_nativeEnableCustomRendering(JNIEnv *env, jobject thiz,jlong nativeHandle,jboolean enable,jint format,jint type
+) {
+    jint result = -1;
+    IArLivePlayer* arLivePlayKit = reinterpret_cast<IArLivePlayer *>(nativeHandle);
+    if (arLivePlayKit != NULL){
+        result = arLivePlayKit->enableCustomRendering(enable,(ArLivePixelFormat)format,(ArLiveBufferType)type);
+    }
+    return (jint)result;
+}
+
+JNIEXPORT jint JNICALL
+Java_io_anyrtc_live_internal_NativeInstance_nativeEnableReceiveSeiMessage(JNIEnv *env, jobject thiz,jlong nativeHandle,jboolean enable,jint payloadType
+) {
+    jint result = -1;
+    IArLivePlayer* arLivePlayKit = reinterpret_cast<IArLivePlayer *>(nativeHandle);
+    if (arLivePlayKit != NULL){
+        result = arLivePlayKit->enableReceiveSeiMessage(enable,payloadType);
+    }
+    return (jint)result;
+}
+
+JNIEXPORT jint JNICALL
+Java_io_anyrtc_live_internal_NativeInstance_nativeSetPlayMode(JNIEnv *env, jobject thiz,jlong nativeHandle,jint mode
+) {
+    jint result = -1;
+    IArLivePlayer* arLivePlayKit = reinterpret_cast<IArLivePlayer *>(nativeHandle);
+    if (arLivePlayKit != NULL){
+        result = arLivePlayKit->setPlayMode(static_cast<ArLivePlayMode>(mode));
+    }
+    return (jint)result;
+}
+
+
+
+JNIEXPORT void JNICALL
+Java_io_anyrtc_live_internal_NativeInstance_nativeShowDebugView(JNIEnv *env, jobject thiz,jlong nativeHandle,jboolean isShow
+) {
+    IArLivePlayer* arLivePlayKit = reinterpret_cast<IArLivePlayer *>(nativeHandle);
+    if (arLivePlayKit != NULL){
+        arLivePlayKit->showDebugView(isShow);
+    }
+}
+
+JNIEXPORT void JNICALL
+Java_io_anyrtc_live_internal_NativeInstance_nativePlayKitRelease(JNIEnv *env, jobject thiz,jlong nativeHandle
+) {
+    InstanceHolder *instance = getInstanceHolder(env, thiz);
+    IArLivePlayer* arLivePlayKit = reinterpret_cast<IArLivePlayer *>(nativeHandle);
+    if (instance->arLiveEngine != NULL){
+       instance->arLiveEngine->releaseArLivePlayer(arLivePlayKit);
+    }
+}
+
+
+
+
+
+
+
+
+JNIEXPORT jlong JNICALL
+Java_io_anyrtc_live_internal_NativeInstance_nativeCreatePlayKit(JNIEnv *env, jobject obj) {
+    InstanceHolder *instance = getInstanceHolder(env, obj);
+    if (instance->arLiveEngine == nullptr){
+        return (jint)-1;
+    }
+    IArLivePlayer* player = instance->arLiveEngine->createArLivePlayer(NULL);
+   return reinterpret_cast<jlong>(player);
+}
+
+JNIEXPORT void JNICALL
+Java_io_anyrtc_live_internal_NativeInstance_nativeSetPlayerobserver(JNIEnv *env, jobject thiz,jlong nativeHandle,jobject observer
+) {
+    IArLivePlayer* arLivePlayKit = reinterpret_cast<IArLivePlayer *>(nativeHandle);
+    if (arLivePlayKit != NULL){
+        LivePlayEvent *event = new LivePlayEvent(observer);
+        arLivePlayKit->setObserver(event);
+    }
+}
+
+
+
+
+JNIEXPORT jint JNICALL
+Java_io_anyrtc_live_internal_NativeInstance_nativeSetPlayerView(JNIEnv *env, jobject obj,jlong nativeHandle,jobject videoSink) {
+
+    IArLivePlayer* arLivePlayKit = reinterpret_cast<IArLivePlayer *>(nativeHandle);
+    jint result = -1;
+    if (arLivePlayKit!= NULL){
+        result = arLivePlayKit->setRenderView(reinterpret_cast<view_t *>(videoSink));
+    }
+    return (jint)result;
+}
+
+}
+
+
+extern "C" {
+JNIEXPORT void JNICALL
+Java_org_webrtc_effector_format_LibYuvBridge_i420ToAbgrInternal(
+        JNIEnv *env,
+        jobject obj,
+        jobject dataYBuffer, jint strideY,
+        jobject dataUBuffer, jint strideU,
+        jobject dataVBuffer, jint strideV,
+        jint width, jint height,
+        jobject outRgbaBuffer)
+{
+    uint8_t *data_y = (uint8_t*) env->GetDirectBufferAddress(dataYBuffer);
+    uint8_t *data_u = (uint8_t*) env->GetDirectBufferAddress(dataUBuffer);
+    uint8_t *data_v = (uint8_t*) env->GetDirectBufferAddress(dataVBuffer);
+    uint8_t *out_rgba = (uint8_t *)(env->GetDirectBufferAddress(outRgbaBuffer));
+
+    int stride_y = strideY;
+    int stride_u = strideU;
+    int stride_v = strideV;
+
+    int dst_stride_rgba = width * 4;
+    int src_width = width;
+    int src_height = height;
+
+    /*
+    LIBYUV_API
+    int I420ToABGR(const uint8_t* src_y,
+                   int src_stride_y,
+                   const uint8_t* src_u,
+                   int src_stride_u,
+                   const uint8_t* src_v,
+                   int src_stride_v,
+                   uint8_t* dst_abgr,
+                   int dst_stride_abgr,
+                   int width,
+                   int height);
+    */
+    libyuv::I420ToABGR(data_y, stride_y,
+               data_u, stride_u,
+               data_v, stride_v,
+               out_rgba, dst_stride_rgba,
+               src_width, src_height);
+}
+
+JNIEXPORT void JNICALL
+Java_org_webrtc_effector_format_LibYuvBridge_abgrToI420Internal(
+        JNIEnv *env,
+        jobject obj,
+        jobject rgbaBuffer,
+        jint width,
+        jint height,
+        jobject outDataYBuffer,
+        jint strideY,
+        jobject outDataUBuffer,
+        jint strideU,
+        jobject outDataVBuffer,
+        jint strideV)
+{
+    uint8_t *rgba = (uint8_t*) env->GetDirectBufferAddress(rgbaBuffer);
+    uint8_t *out_data_y = (uint8_t*) env->GetDirectBufferAddress(outDataYBuffer);
+    uint8_t *out_data_u = (uint8_t*) env->GetDirectBufferAddress(outDataUBuffer);
+    uint8_t *out_data_v = (uint8_t*) env->GetDirectBufferAddress(outDataVBuffer);
+
+    /*
+    // ABGR little endian (rgba in memory) to I420.
+    LIBYUV_API
+    int ABGRToI420(const uint8* src_frame, int src_stride_frame,
+                   uint8* dst_y, int dst_stride_y,
+                   uint8* dst_u, int dst_stride_u,
+                   uint8* dst_v, int dst_stride_v,
+                   int width, int height);
+    */
+    libyuv::ABGRToI420(rgba, width * 4,
+               out_data_y, strideY,
+               out_data_u, strideU,
+               out_data_v, strideV,
+               width, height);
+}
+
+
+
+}
+
+
+//device manager
+extern "C"{
+JNIEXPORT jboolean JNICALL
+Java_io_anyrtc_live_internal_NativeInstance_enableCameraTorch(JNIEnv *env, jobject thiz,
+                                                              jboolean enable) {
+    return AndroidDeviceManager::Inst().enableCameraTorch(enable);
+}
+
+JNIEXPORT jfloat JNICALL
+Java_io_anyrtc_live_internal_NativeInstance_getCameraZoomMaxRatio(JNIEnv *env, jobject thiz) {
+    return  AndroidDeviceManager::Inst().getCameraZoomMaxRatio();
+}
+
+JNIEXPORT jint JNICALL
+Java_io_anyrtc_live_internal_NativeInstance_setCameraZoomRatio(JNIEnv *env, jobject thiz,
+                                                               jfloat var1) {
+    return  AndroidDeviceManager::Inst().setCameraZoomRatio(var1);
+}
+
+JNIEXPORT jboolean JNICALL
+Java_io_anyrtc_live_internal_NativeInstance_isAutoFocusEnabled(JNIEnv *env, jobject thiz) {
+    return AndroidDeviceManager::Inst().isAutoFocusEnabled();
+}
+
+JNIEXPORT jint JNICALL
+Java_io_anyrtc_live_internal_NativeInstance_enableCameraAutoFocus(JNIEnv *env, jobject thiz,
+                                                                  jboolean var1) {
+    return AndroidDeviceManager::Inst().enableCameraAutoFocus(var1);
+}
+
+JNIEXPORT void JNICALL
+Java_io_anyrtc_live_internal_NativeInstance_setCameraCapturerParam(JNIEnv *env, jobject thiz,
+                                                                   jint mode, jint width,
+                                                                   jint height) {
+    AndroidDeviceManager::Inst().setCameraCapturerParam(mode,width,height);
+}
+JNIEXPORT jint JNICALL
+Java_io_anyrtc_live_internal_NativeInstance_setCameraFocusPosition(JNIEnv *env, jobject thiz,
+                                                                   jfloat var1, jfloat var2) {
+    return AndroidDeviceManager::Inst().setCameraFocusPosition(var1,var2);
+}
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+extern "C"
+JNIEXPORT void JNICALL
+Java_org_webrtc_Camera1Session_recoverCamera(JNIEnv *env, jobject thiz) {
+    AndroidDeviceManager::Inst().recoverCamera();
+}

+ 15 - 0
liveplayer/src/main/cpp/jni/PlatformContext.h

@@ -0,0 +1,15 @@
+//
+// Created by liu on 2021/9/18.
+//
+
+#ifndef LIVEPLAYER_PLATFORMCONTEXT_H
+#define LIVEPLAYER_PLATFORMCONTEXT_H
+
+namespace arlive {
+  class PlatformContext {
+    public:
+        virtual ~PlatformContext() = default;
+    };
+}
+
+#endif //LIVEPLAYER_PLATFORMCONTEXT_H

+ 132 - 0
liveplayer/src/main/cpp/jni/StaticThreads.cpp

@@ -0,0 +1,132 @@
+#include "StaticThreads.h"
+
+#include "rtc_base/thread.h"
+#include "call/call.h"
+
+#include <mutex>
+#include <algorithm>
+
+namespace arlive {
+
+template <class ValueT, class CreatorT>
+class Pool : public std::enable_shared_from_this<Pool<ValueT, CreatorT>> {
+  struct Entry {
+    std::unique_ptr<ValueT> value;
+    size_t refcnt;
+
+    bool operator < (const Entry &other) const {
+      return refcnt < other.refcnt;
+    }
+  };
+
+public:
+  explicit Pool(CreatorT creator) : creator_(std::move(creator)) {
+  }
+  std::shared_ptr<ValueT> get() {
+    std::unique_lock<std::mutex> lock(mutex_);
+    set_pool_size_locked(1);
+    auto i = std::min_element(entries_.begin(), entries_.end()) - entries_.begin();
+    return std::shared_ptr<ValueT>(entries_[i].value.get(),
+      [i, self = this->shared_from_this()](auto *ptr) {
+        self->dec_ref(i);
+      });
+  }
+
+  void set_pool_size(size_t size) {
+    std::unique_lock<std::mutex> lock(mutex_);
+    set_pool_size_locked(size);
+  }
+
+  void dec_ref(size_t i) {
+    std::unique_lock<std::mutex> lock(mutex_);
+    entries_.at(i).refcnt--;
+  }
+
+private:
+  std::mutex mutex_;
+  std::vector<Entry> entries_;
+
+  CreatorT creator_;
+
+  void set_pool_size_locked(size_t size) {
+    for (size_t i = entries_.size(); i < size; i++) {
+      entries_.emplace_back(Entry{creator_(i + 1), 0});
+    }
+  }
+};
+
+class ThreadsImpl : public Threads {
+  using Thread = std::unique_ptr<rtc::Thread>;
+public:
+  explicit ThreadsImpl(size_t i) {
+    auto suffix = i == 0 ? "" : "#" + std::to_string(i);
+    media_ = create("arlive-media" + suffix);
+    worker_ = create("arlive-work"  + suffix);
+    worker_->DisallowAllInvokes();
+  }
+
+  rtc::Thread *getMediaThread() override {
+    return media_.get();
+  }
+  rtc::Thread *getWorkerThread() override {
+    return worker_.get();
+  }
+  rtc::scoped_refptr<webrtc::SharedModuleThread> getSharedModuleThread() override {
+    // This function must be called from a single thread because of SharedModuleThread implementation
+    // So we don't care about making it thread safe
+    if (!shared_module_thread_) {
+      shared_module_thread_ = webrtc::SharedModuleThread::Create(
+          webrtc::ProcessThread::Create("arlive-module"),
+          [=] { shared_module_thread_ = nullptr; });
+    }
+    return shared_module_thread_;
+  }
+
+private:
+  Thread media_;
+  Thread worker_;
+  rtc::scoped_refptr<webrtc::SharedModuleThread> shared_module_thread_;
+
+  static Thread create(const std::string &name) {
+    return init(std::unique_ptr<rtc::Thread>(rtc::Thread::Create()), name);
+  }
+  static Thread init(Thread value, const std::string &name) {
+    value->SetName(name, nullptr);
+    value->Start();
+    return value;
+  }
+};
+
+class ThreadsCreator {
+public:
+  std::unique_ptr<Threads> operator()(size_t i) {
+   return std::make_unique<ThreadsImpl>(i);
+  }
+};
+
+Pool<Threads, ThreadsCreator> &get_pool() {
+  static auto pool = std::make_shared<Pool<Threads, ThreadsCreator>>(ThreadsCreator());
+  return *pool;
+}
+
+void Threads::setPoolSize(size_t size){
+  get_pool().set_pool_size(size);
+}
+std::shared_ptr<Threads> Threads::getThreads(){
+  return get_pool().get();
+}
+
+namespace StaticThreads {
+
+
+rtc::Thread *getMediaThread() {
+  return getThreads()->getMediaThread();
+}
+
+std::shared_ptr<Threads> &getThreads() {
+  static std::shared_ptr<Threads> threads = std::make_shared<ThreadsImpl>(0);
+  return threads;
+}
+};
+
+}

+ 36 - 0
liveplayer/src/main/cpp/jni/StaticThreads.h

@@ -0,0 +1,36 @@
+#pragma once
+
+#include <cstddef>
+#include <memory>
+
+namespace rtc {
+class Thread;
+template <class T>
+class scoped_refptr;
+}
+namespace webrtc {
+class SharedModuleThread;
+}
+
+namespace arlive {
+
+class Threads {
+public:
+  virtual ~Threads() = default;
+  virtual rtc::Thread *getMediaThread() = 0;
+  virtual rtc::Thread *getWorkerThread() = 0;
+  virtual rtc::scoped_refptr<webrtc::SharedModuleThread> getSharedModuleThread() = 0;
+
+  // it is not possible to decrease pool size
+  static void setPoolSize(size_t size);
+  static std::shared_ptr<Threads> getThreads();
+};
+
+namespace StaticThreads {
+rtc::Thread *getMediaThread();
+rtc::Thread *getWorkerThread();
+rtc::scoped_refptr<webrtc::SharedModuleThread> getSharedMoudleThread();
+std::shared_ptr<Threads> &getThreads();
+}
+
+};

Некоторые файлы не были показаны из-за большого количества измененных файлов