expo-libvlc-player 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +2 -0
- package/README.md +107 -0
- package/android/.gradle/9.0-milestone-1/checksums/checksums.lock +0 -0
- package/android/.gradle/9.0-milestone-1/fileChanges/last-build.bin +0 -0
- package/android/.gradle/9.0-milestone-1/fileHashes/fileHashes.lock +0 -0
- package/android/.gradle/9.0-milestone-1/gc.properties +0 -0
- package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
- package/android/.gradle/buildOutputCleanup/cache.properties +2 -0
- package/android/.gradle/config.properties +2 -0
- package/android/.gradle/ideaInitScripts/ijtgtmapper.gradle +3 -0
- package/android/.gradle/vcs-1/gc.properties +0 -0
- package/android/.idea/AndroidProjectSystem.xml +6 -0
- package/android/.idea/android.iml +9 -0
- package/android/.idea/caches/deviceStreaming.xml +835 -0
- package/android/.idea/deviceManager.xml +13 -0
- package/android/.idea/gradle.xml +13 -0
- package/android/.idea/misc.xml +3 -0
- package/android/.idea/modules.xml +8 -0
- package/android/.idea/runConfigurations.xml +17 -0
- package/android/.idea/vcs.xml +6 -0
- package/android/.idea/workspace.xml +72 -0
- package/android/build.gradle +53 -0
- package/android/local.properties +8 -0
- package/android/proguard-rules.pro +19 -0
- package/android/src/main/AndroidManifest.xml +4 -0
- package/android/src/main/java/expo/modules/libvlcplayer/AudioFocusManager.kt +210 -0
- package/android/src/main/java/expo/modules/libvlcplayer/VlcPlayerManager.kt +82 -0
- package/android/src/main/java/expo/modules/libvlcplayer/VlcPlayerModule.kt +129 -0
- package/android/src/main/java/expo/modules/libvlcplayer/VlcPlayerView.kt +328 -0
- package/android/src/main/java/expo/modules/libvlcplayer/enums/AudioMixingMode.kt +20 -0
- package/app.plugin.js +1 -0
- package/build/VlcPlayer.types.d.ts +278 -0
- package/build/VlcPlayer.types.d.ts.map +1 -0
- package/build/VlcPlayer.types.js +2 -0
- package/build/VlcPlayer.types.js.map +1 -0
- package/build/VlcPlayerModule.d.ts +6 -0
- package/build/VlcPlayerModule.d.ts.map +1 -0
- package/build/VlcPlayerModule.js +4 -0
- package/build/VlcPlayerModule.js.map +1 -0
- package/build/VlcPlayerView.d.ts +5 -0
- package/build/VlcPlayerView.d.ts.map +1 -0
- package/build/VlcPlayerView.js +36 -0
- package/build/VlcPlayerView.js.map +1 -0
- package/build/index.d.ts +4 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +5 -0
- package/build/index.js.map +1 -0
- package/build/utils/props.d.ts +3 -0
- package/build/utils/props.d.ts.map +1 -0
- package/build/utils/props.js +11 -0
- package/build/utils/props.js.map +1 -0
- package/eslint.config.js +50 -0
- package/expo-module.config.json +16 -0
- package/ios/Enums/AudioMixingMode.swift +35 -0
- package/ios/ExpoLibVlcPlayer.podspec +29 -0
- package/ios/VlcPlayerManager.swift +116 -0
- package/ios/VlcPlayerModule.swift +110 -0
- package/ios/VlcPlayerView.swift +321 -0
- package/package.json +49 -0
- package/plugin/build/withExpoLibVlcPlayer.d.ts +3 -0
- package/plugin/build/withExpoLibVlcPlayer.js +18 -0
- package/plugin/src/withExpoLibVlcPlayer.ts +21 -0
- package/plugin/tsconfig.json +9 -0
- package/plugin/tsconfig.tsbuildinfo +1 -0
- package/src/VlcPlayer.types.ts +291 -0
- package/src/VlcPlayerModule.ts +7 -0
- package/src/VlcPlayerView.tsx +73 -0
- package/src/index.ts +4 -0
- package/src/utils/props.ts +20 -0
- package/tsconfig.json +9 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<project version="4">
|
|
3
|
+
<component name="DeviceTable">
|
|
4
|
+
<option name="columnSorters">
|
|
5
|
+
<list>
|
|
6
|
+
<ColumnSorterState>
|
|
7
|
+
<option name="column" value="Name" />
|
|
8
|
+
<option name="order" value="ASCENDING" />
|
|
9
|
+
</ColumnSorterState>
|
|
10
|
+
</list>
|
|
11
|
+
</option>
|
|
12
|
+
</component>
|
|
13
|
+
</project>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<project version="4">
|
|
3
|
+
<component name="GradleMigrationSettings" migrationVersion="1" />
|
|
4
|
+
<component name="GradleSettings">
|
|
5
|
+
<option name="linkedExternalProjectsSettings">
|
|
6
|
+
<GradleProjectSettings>
|
|
7
|
+
<option name="testRunner" value="CHOOSE_PER_TEST" />
|
|
8
|
+
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
|
9
|
+
<option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
|
|
10
|
+
</GradleProjectSettings>
|
|
11
|
+
</option>
|
|
12
|
+
</component>
|
|
13
|
+
</project>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<project version="4">
|
|
3
|
+
<component name="RunConfigurationProducerService">
|
|
4
|
+
<option name="ignoredProducers">
|
|
5
|
+
<set>
|
|
6
|
+
<option value="com.intellij.execution.junit.AbstractAllInDirectoryConfigurationProducer" />
|
|
7
|
+
<option value="com.intellij.execution.junit.AllInPackageConfigurationProducer" />
|
|
8
|
+
<option value="com.intellij.execution.junit.PatternConfigurationProducer" />
|
|
9
|
+
<option value="com.intellij.execution.junit.TestInClassConfigurationProducer" />
|
|
10
|
+
<option value="com.intellij.execution.junit.UniqueIdConfigurationProducer" />
|
|
11
|
+
<option value="com.intellij.execution.junit.testDiscovery.JUnitTestDiscoveryConfigurationProducer" />
|
|
12
|
+
<option value="org.jetbrains.kotlin.idea.junit.KotlinJUnitRunConfigurationProducer" />
|
|
13
|
+
<option value="org.jetbrains.kotlin.idea.junit.KotlinPatternConfigurationProducer" />
|
|
14
|
+
</set>
|
|
15
|
+
</option>
|
|
16
|
+
</component>
|
|
17
|
+
</project>
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<project version="4">
|
|
3
|
+
<component name="AutoImportSettings">
|
|
4
|
+
<option name="autoReloadType" value="NONE" />
|
|
5
|
+
</component>
|
|
6
|
+
<component name="ChangeListManager">
|
|
7
|
+
<list default="true" id="ce1cf2d7-3e2c-41b3-ae06-62438ddceca7" name="Changes" comment="" />
|
|
8
|
+
<option name="SHOW_DIALOG" value="false" />
|
|
9
|
+
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
|
10
|
+
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
|
11
|
+
<option name="LAST_RESOLUTION" value="IGNORE" />
|
|
12
|
+
</component>
|
|
13
|
+
<component name="ClangdSettings">
|
|
14
|
+
<option name="formatViaClangd" value="false" />
|
|
15
|
+
</component>
|
|
16
|
+
<component name="ExternalProjectsManager">
|
|
17
|
+
<system id="GRADLE">
|
|
18
|
+
<state>
|
|
19
|
+
<projects_view>
|
|
20
|
+
<tree_state>
|
|
21
|
+
<expand />
|
|
22
|
+
<select />
|
|
23
|
+
</tree_state>
|
|
24
|
+
</projects_view>
|
|
25
|
+
</state>
|
|
26
|
+
</system>
|
|
27
|
+
</component>
|
|
28
|
+
<component name="FormatOnSaveOptions">
|
|
29
|
+
<option name="myRunOnSave" value="true" />
|
|
30
|
+
</component>
|
|
31
|
+
<component name="Git.Settings">
|
|
32
|
+
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$/.." />
|
|
33
|
+
</component>
|
|
34
|
+
<component name="ProblemsViewState">
|
|
35
|
+
<option name="selectedTabId" value="ProjectErrors" />
|
|
36
|
+
</component>
|
|
37
|
+
<component name="ProjectColorInfo">{
|
|
38
|
+
"associatedIndex": 0
|
|
39
|
+
}</component>
|
|
40
|
+
<component name="ProjectId" id="2z2OVb2mI7QaoiYFjJWlUUfJ6HB" />
|
|
41
|
+
<component name="ProjectViewState">
|
|
42
|
+
<option name="hideEmptyMiddlePackages" value="true" />
|
|
43
|
+
<option name="showLibraryContents" value="true" />
|
|
44
|
+
</component>
|
|
45
|
+
<component name="PropertiesComponent"><![CDATA[{
|
|
46
|
+
"keyToString": {
|
|
47
|
+
"ModuleVcsDetector.initialDetectionPerformed": "true",
|
|
48
|
+
"RunOnceActivity.ShowReadmeOnStart": "true",
|
|
49
|
+
"RunOnceActivity.cidr.known.project.marker": "true",
|
|
50
|
+
"RunOnceActivity.git.unshallow": "true",
|
|
51
|
+
"RunOnceActivity.readMode.enableVisualFormatting": "true",
|
|
52
|
+
"android.gradle.sync.needed": "true",
|
|
53
|
+
"cf.first.check.clang-format": "false",
|
|
54
|
+
"cidr.known.project.marker": "true",
|
|
55
|
+
"git-widget-placeholder": "0.1.0",
|
|
56
|
+
"ignore.virus.scanning.warn.message": "true",
|
|
57
|
+
"kotlin-language-version-configured": "true",
|
|
58
|
+
"last_opened_file_path": "//wsl.localhost/Ubuntu/home/rarbit/dev/expo-libvlc-player/android",
|
|
59
|
+
"settings.editor.selected.configurable": "preferences.sourceCode"
|
|
60
|
+
}
|
|
61
|
+
}]]></component>
|
|
62
|
+
<component name="TaskManager">
|
|
63
|
+
<task active="true" id="Default" summary="Default task">
|
|
64
|
+
<changelist id="ce1cf2d7-3e2c-41b3-ae06-62438ddceca7" name="Changes" comment="" />
|
|
65
|
+
<created>1750925729512</created>
|
|
66
|
+
<option name="number" value="Default" />
|
|
67
|
+
<option name="presentableId" value="Default" />
|
|
68
|
+
<updated>1750925729512</updated>
|
|
69
|
+
</task>
|
|
70
|
+
<servers />
|
|
71
|
+
</component>
|
|
72
|
+
</project>
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
apply plugin: 'com.android.library'
|
|
2
|
+
|
|
3
|
+
group = 'expo.modules.libvlcplayer'
|
|
4
|
+
version = '0.1.0'
|
|
5
|
+
|
|
6
|
+
def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
|
|
7
|
+
apply from: expoModulesCorePlugin
|
|
8
|
+
applyKotlinExpoModulesCorePlugin()
|
|
9
|
+
useCoreDependencies()
|
|
10
|
+
useExpoPublishing()
|
|
11
|
+
|
|
12
|
+
// If you want to use the managed Android SDK versions from expo-modules-core, set this to true.
|
|
13
|
+
// The Android SDK versions will be bumped from time to time in SDK releases and may introduce breaking changes in your module code.
|
|
14
|
+
// Most of the time, you may like to manage the Android SDK versions yourself.
|
|
15
|
+
def useManagedAndroidSdkVersions = false
|
|
16
|
+
if (useManagedAndroidSdkVersions) {
|
|
17
|
+
useDefaultAndroidSdkVersions()
|
|
18
|
+
} else {
|
|
19
|
+
buildscript {
|
|
20
|
+
// Simple helper that allows the root project to override versions declared by this library.
|
|
21
|
+
ext.safeExtGet = { prop, fallback ->
|
|
22
|
+
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
project.android {
|
|
26
|
+
compileSdkVersion safeExtGet("compileSdkVersion", 34)
|
|
27
|
+
defaultConfig {
|
|
28
|
+
minSdkVersion safeExtGet("minSdkVersion", 21)
|
|
29
|
+
targetSdkVersion safeExtGet("targetSdkVersion", 34)
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
android {
|
|
35
|
+
namespace "expo.modules.libvlcplayer"
|
|
36
|
+
defaultConfig {
|
|
37
|
+
versionCode 1
|
|
38
|
+
versionName "0.1.0"
|
|
39
|
+
consumerProguardFiles("proguard-rules.pro")
|
|
40
|
+
}
|
|
41
|
+
lintOptions {
|
|
42
|
+
abortOnError false
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
repositories {
|
|
47
|
+
mavenCentral()
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
dependencies {
|
|
51
|
+
implementation 'com.facebook.react:react-native:+'
|
|
52
|
+
implementation 'org.videolan.android:libvlc-all:3.6.2'
|
|
53
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
## This file must *NOT* be checked into Version Control Systems,
|
|
2
|
+
# as it contains information specific to your local configuration.
|
|
3
|
+
#
|
|
4
|
+
# Location of the SDK. This is only used by Gradle.
|
|
5
|
+
# For customization when using a Version Control System, please read the
|
|
6
|
+
# header note.
|
|
7
|
+
#Thu Jun 26 02:15:26 CST 2025
|
|
8
|
+
sdk.dir=C\:\\Users\\rarbit\\AppData\\Local\\Android\\Sdk
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Add project specific ProGuard rules here.
|
|
2
|
+
# By default, the flags in this file are appended to flags specified
|
|
3
|
+
# in /home/tom/work/android/sdk/tools/proguard/proguard-android.txt
|
|
4
|
+
# You can edit the include path and order by changing the proguardFiles
|
|
5
|
+
# directive in build.gradle.
|
|
6
|
+
#
|
|
7
|
+
# For more details, see
|
|
8
|
+
# http://developer.android.com/guide/developing/tools/proguard.html
|
|
9
|
+
|
|
10
|
+
# Add any project specific keep options here:
|
|
11
|
+
|
|
12
|
+
# If your project uses WebView with JS, uncomment the following
|
|
13
|
+
# and specify the fully qualified class name to the JavaScript interface
|
|
14
|
+
# class:
|
|
15
|
+
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
|
16
|
+
# public *;
|
|
17
|
+
#}
|
|
18
|
+
|
|
19
|
+
-keep class org.videolan.libvlc.** { *; }
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
package expo.modules.libvlcplayer
|
|
2
|
+
|
|
3
|
+
import kotlinx.coroutines.launch
|
|
4
|
+
|
|
5
|
+
import java.lang.ref.WeakReference
|
|
6
|
+
|
|
7
|
+
import expo.modules.libvlcplayer.enums.AudioMixingMode
|
|
8
|
+
|
|
9
|
+
import android.content.Context
|
|
10
|
+
import android.media.AudioAttributes
|
|
11
|
+
import android.media.AudioFocusRequest
|
|
12
|
+
import android.media.AudioManager
|
|
13
|
+
import android.os.Build
|
|
14
|
+
|
|
15
|
+
import expo.modules.kotlin.AppContext
|
|
16
|
+
import expo.modules.kotlin.exception.CodedException
|
|
17
|
+
import expo.modules.kotlin.types.Enumerable
|
|
18
|
+
|
|
19
|
+
import org.videolan.libvlc.MediaPlayer
|
|
20
|
+
|
|
21
|
+
class AudioFocusManager(
|
|
22
|
+
private val appContext: AppContext,
|
|
23
|
+
private var views: MutableList<WeakReference<VlcPlayerView>>
|
|
24
|
+
) :
|
|
25
|
+
AudioManager.OnAudioFocusChangeListener {
|
|
26
|
+
private val audioManager by lazy {
|
|
27
|
+
appContext.reactContext?.getSystemService(Context.AUDIO_SERVICE) as? AudioManager ?: run {
|
|
28
|
+
throw CodedException("Failed to get AudioFocusManager service")
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
private var currentFocusRequest: AudioFocusRequest? = null
|
|
33
|
+
private var currentMixingMode: AudioMixingMode = AudioMixingMode.MIX_WITH_OTHERS
|
|
34
|
+
private val anyPlayerRequiresFocus: Boolean
|
|
35
|
+
get() = views.toList().any { weakView ->
|
|
36
|
+
weakView.get()?.let { view ->
|
|
37
|
+
playerRequiresFocus(view.mediaPlayer)
|
|
38
|
+
} ?: false
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
private var previousVolume: Int = MAX_PLAYER_VOLUME
|
|
42
|
+
|
|
43
|
+
private fun playerRequiresFocus(player: MediaPlayer?): Boolean {
|
|
44
|
+
val mPlayer = player ?: return false
|
|
45
|
+
return mPlayer.isPlaying() && mPlayer.getVolume() > MIN_PLAYER_VOLUME
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
private fun findAudioMixingMode(): AudioMixingMode {
|
|
49
|
+
val mixingModes = views.toList().mapNotNull { view ->
|
|
50
|
+
view.get()?.takeIf { it.mediaPlayer?.isPlaying() == true }?.audioMixingMode
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (mixingModes.isEmpty()) {
|
|
54
|
+
return AudioMixingMode.MIX_WITH_OTHERS
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return mixingModes.reduce { currentAudioMixingMode, next ->
|
|
58
|
+
next.takeIf { it.priority > currentAudioMixingMode.priority } ?: currentAudioMixingMode
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
private fun requestAudioFocus() {
|
|
63
|
+
val audioMixingMode = findAudioMixingMode()
|
|
64
|
+
|
|
65
|
+
if (audioMixingMode == AudioMixingMode.MIX_WITH_OTHERS || !anyPlayerRequiresFocus) {
|
|
66
|
+
abandonAudioFocus()
|
|
67
|
+
currentMixingMode = audioMixingMode
|
|
68
|
+
return
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
val audioFocusType = when (currentMixingMode) {
|
|
72
|
+
AudioMixingMode.DUCK_OTHERS -> AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK
|
|
73
|
+
AudioMixingMode.AUTO -> AudioManager.AUDIOFOCUS_GAIN
|
|
74
|
+
AudioMixingMode.DO_NOT_MIX -> AudioManager.AUDIOFOCUS_GAIN
|
|
75
|
+
else -> AudioManager.AUDIOFOCUS_GAIN
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
79
|
+
currentFocusRequest?.let {
|
|
80
|
+
if (it.focusGain == audioFocusType) {
|
|
81
|
+
return
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
val newFocusRequest = AudioFocusRequest.Builder(audioFocusType).run {
|
|
86
|
+
setAudioAttributes(
|
|
87
|
+
AudioAttributes.Builder().run {
|
|
88
|
+
setUsage(AudioAttributes.USAGE_MEDIA)
|
|
89
|
+
setContentType(AudioAttributes.CONTENT_TYPE_MOVIE)
|
|
90
|
+
setOnAudioFocusChangeListener(this@AudioFocusManager)
|
|
91
|
+
build()
|
|
92
|
+
}
|
|
93
|
+
).build()
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
currentFocusRequest = newFocusRequest
|
|
97
|
+
audioManager.requestAudioFocus(newFocusRequest)
|
|
98
|
+
} else {
|
|
99
|
+
@Suppress("DEPRECATION")
|
|
100
|
+
audioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC, audioFocusType)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
currentMixingMode = audioMixingMode
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
private fun abandonAudioFocus() {
|
|
107
|
+
currentFocusRequest?.let {
|
|
108
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
109
|
+
audioManager.abandonAudioFocusRequest(it)
|
|
110
|
+
} else {
|
|
111
|
+
@Suppress("DEPRECATION")
|
|
112
|
+
audioManager.abandonAudioFocus(this)
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
currentFocusRequest = null
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
fun updateAudioFocus() {
|
|
120
|
+
if (anyPlayerRequiresFocus || findAudioMixingMode() != currentMixingMode) {
|
|
121
|
+
requestAudioFocus()
|
|
122
|
+
} else {
|
|
123
|
+
abandonAudioFocus()
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
override fun onAudioFocusChange(focusChange: Int) {
|
|
128
|
+
when (focusChange) {
|
|
129
|
+
AudioManager.AUDIOFOCUS_LOSS -> {
|
|
130
|
+
appContext.mainQueue.launch {
|
|
131
|
+
views.forEach { view ->
|
|
132
|
+
pausePlayerIfUnmuted(view.get()?.mediaPlayer)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
currentFocusRequest = null
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
AudioManager.AUDIOFOCUS_LOSS_TRANSIENT -> {
|
|
140
|
+
val audioMixingMode = findAudioMixingMode()
|
|
141
|
+
|
|
142
|
+
if (audioMixingMode == AudioMixingMode.MIX_WITH_OTHERS) {
|
|
143
|
+
return
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
appContext.mainQueue.launch {
|
|
147
|
+
views.forEach { view ->
|
|
148
|
+
pausePlayerIfUnmuted(view.get()?.mediaPlayer)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
currentFocusRequest = null
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK -> {
|
|
156
|
+
val audioMixingMode = findAudioMixingMode()
|
|
157
|
+
|
|
158
|
+
appContext.mainQueue.launch {
|
|
159
|
+
views.forEach { view ->
|
|
160
|
+
view.get()?.mediaPlayer?.let { player ->
|
|
161
|
+
if (audioMixingMode == AudioMixingMode.DO_NOT_MIX) {
|
|
162
|
+
pausePlayerIfUnmuted(player)
|
|
163
|
+
} else {
|
|
164
|
+
duckPlayer(player)
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
AudioManager.AUDIOFOCUS_GAIN -> {
|
|
172
|
+
appContext.mainQueue.launch {
|
|
173
|
+
views.forEach { view ->
|
|
174
|
+
unduckPlayer(view.get()?.mediaPlayer)
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
private fun pausePlayerIfUnmuted(player: MediaPlayer?) {
|
|
182
|
+
player?.let { mediaPlayer ->
|
|
183
|
+
if (mediaPlayer.getVolume() > MIN_PLAYER_VOLUME) {
|
|
184
|
+
appContext.mainQueue.launch {
|
|
185
|
+
mediaPlayer.pause()
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
private fun duckPlayer(player: MediaPlayer?) {
|
|
192
|
+
player?.let { weakPlayer ->
|
|
193
|
+
appContext.mainQueue.launch {
|
|
194
|
+
val volume = weakPlayer.getVolume() / 20
|
|
195
|
+
weakPlayer.setVolume(volume)
|
|
196
|
+
previousVolume = volume
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
private fun unduckPlayer(player: MediaPlayer?) {
|
|
202
|
+
player?.let { weakPlayer ->
|
|
203
|
+
if (weakPlayer.getVolume() > MIN_PLAYER_VOLUME) {
|
|
204
|
+
appContext.mainQueue.launch {
|
|
205
|
+
weakPlayer.setVolume(previousVolume)
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
package expo.modules.libvlcplayer
|
|
2
|
+
|
|
3
|
+
import java.lang.ref.WeakReference
|
|
4
|
+
|
|
5
|
+
import expo.modules.kotlin.AppContext
|
|
6
|
+
import expo.modules.kotlin.exception.Exceptions
|
|
7
|
+
|
|
8
|
+
import org.videolan.libvlc.MediaPlayer
|
|
9
|
+
|
|
10
|
+
object VlcPlayerManager {
|
|
11
|
+
private var views: MutableList<WeakReference<VlcPlayerView>> = mutableListOf()
|
|
12
|
+
|
|
13
|
+
lateinit var audioFocusManager: AudioFocusManager
|
|
14
|
+
|
|
15
|
+
fun onModuleCreated(appContext: AppContext) {
|
|
16
|
+
val context = appContext.reactContext ?: throw Exceptions.ReactContextLost()
|
|
17
|
+
|
|
18
|
+
if (!this::audioFocusManager.isInitialized) {
|
|
19
|
+
audioFocusManager = AudioFocusManager(appContext, views)
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
fun registerView(view: VlcPlayerView) {
|
|
24
|
+
views.find { it.get() == view } ?: run { views.add(WeakReference(view)) }
|
|
25
|
+
audioFocusManager.updateAudioFocus()
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
fun unregisterView(view: VlcPlayerView) {
|
|
29
|
+
views.removeAll { it.get() == view }
|
|
30
|
+
audioFocusManager.updateAudioFocus()
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
fun onAppDestroyed() {
|
|
34
|
+
views.forEach { view ->
|
|
35
|
+
view.get()?.destroyPlayer()
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
fun onViewDestroyed(view: VlcPlayerView) {
|
|
40
|
+
view.destroyPlayer()
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
fun onAppForegrounded() {
|
|
44
|
+
views.forEach { view ->
|
|
45
|
+
view.get()?.let { playerView ->
|
|
46
|
+
playerView.mediaPlayer?.let { player ->
|
|
47
|
+
player.attachViews(
|
|
48
|
+
playerView.videoLayout,
|
|
49
|
+
null,
|
|
50
|
+
ENABLE_SUBTITLES,
|
|
51
|
+
USE_TEXTURE_VIEW
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
if (!player.isPlaying()) {
|
|
55
|
+
val time = player.getTime()
|
|
56
|
+
val error = (-1).toLong()
|
|
57
|
+
|
|
58
|
+
if (time != error) {
|
|
59
|
+
player.setTime(time)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
fun onAppBackgrounded() {
|
|
68
|
+
views.forEach { view ->
|
|
69
|
+
view.get()?.let { playerView ->
|
|
70
|
+
playerView.onBackground(mapOf())
|
|
71
|
+
|
|
72
|
+
playerView.mediaPlayer?.let { player ->
|
|
73
|
+
if (!playerView.playInBackground && player.isPlaying()) {
|
|
74
|
+
player.pause()
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
player.detachViews()
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
package expo.modules.libvlcplayer
|
|
2
|
+
|
|
3
|
+
import expo.modules.libvlcplayer.enums.AudioMixingMode
|
|
4
|
+
|
|
5
|
+
import com.facebook.react.bridge.ReadableMap
|
|
6
|
+
|
|
7
|
+
import expo.modules.kotlin.modules.Module
|
|
8
|
+
import expo.modules.kotlin.modules.ModuleDefinition
|
|
9
|
+
|
|
10
|
+
private const val BUFFERING_EVENT = "onBuffering"
|
|
11
|
+
private const val PLAYING_EVENT = "onPlaying"
|
|
12
|
+
private const val PAUSED_EVENT = "onPaused"
|
|
13
|
+
private const val STOPPED_EVENT = "onStopped"
|
|
14
|
+
private const val ENDED_EVENT = "onEnded"
|
|
15
|
+
private const val REPEAT_EVENT = "onRepeat"
|
|
16
|
+
private const val WARN_EVENT = "onWarn"
|
|
17
|
+
private const val ERROR_EVENT = "onError"
|
|
18
|
+
private const val POSITION_CHANGED_EVENT = "onPositionChanged"
|
|
19
|
+
private const val LOAD_EVENT = "onLoad"
|
|
20
|
+
private const val BACKGROUND_EVENT = "onBackground"
|
|
21
|
+
|
|
22
|
+
val playerEvents = arrayOf(
|
|
23
|
+
BUFFERING_EVENT,
|
|
24
|
+
PLAYING_EVENT,
|
|
25
|
+
PAUSED_EVENT,
|
|
26
|
+
STOPPED_EVENT,
|
|
27
|
+
ENDED_EVENT,
|
|
28
|
+
REPEAT_EVENT,
|
|
29
|
+
WARN_EVENT,
|
|
30
|
+
ERROR_EVENT,
|
|
31
|
+
POSITION_CHANGED_EVENT,
|
|
32
|
+
LOAD_EVENT,
|
|
33
|
+
BACKGROUND_EVENT
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
class VlcPlayerModule : Module() {
|
|
37
|
+
override fun definition() = ModuleDefinition {
|
|
38
|
+
Name("ExpoLibVlcPlayer")
|
|
39
|
+
|
|
40
|
+
OnCreate {
|
|
41
|
+
VlcPlayerManager.onModuleCreated(appContext)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
OnDestroy {
|
|
45
|
+
VlcPlayerManager.onAppDestroyed()
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
View(VlcPlayerView::class) {
|
|
49
|
+
Events(playerEvents)
|
|
50
|
+
|
|
51
|
+
Prop("uri") { view: VlcPlayerView, uri: String ->
|
|
52
|
+
view.uri = uri
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
Prop("subtitle") { view: VlcPlayerView, subtitle: ReadableMap? ->
|
|
56
|
+
view.setSubtitle(subtitle)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
Prop("options") { view: VlcPlayerView, options: ArrayList<String>? ->
|
|
60
|
+
view.options = options ?: ArrayList<String>()
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
Prop("volume") { view: VlcPlayerView, volume: Int? ->
|
|
64
|
+
view.setVolume(volume ?: MAX_PLAYER_VOLUME)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
Prop("mute") { view: VlcPlayerView, mute: Boolean? ->
|
|
68
|
+
view.setMute(mute ?: false)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
Prop("rate") { view: VlcPlayerView, rate: Float? ->
|
|
72
|
+
view.setRate(rate ?: DEFAULT_PLAYER_RATE)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
Prop("tracks") { view: VlcPlayerView, tracks: ReadableMap? ->
|
|
76
|
+
view.setTracks(tracks)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
Prop("repeat") { view: VlcPlayerView, repeat: Boolean? ->
|
|
80
|
+
view.setRepeat(repeat ?: false)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
Prop("aspectRatio") { view: VlcPlayerView, aspectRatio: String? ->
|
|
84
|
+
view.setAspectRatio(aspectRatio)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
Prop("audioMixingMode") { view: VlcPlayerView, audioMixingMode: AudioMixingMode ->
|
|
88
|
+
view.audioMixingMode = audioMixingMode
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
Prop("playInBackground") { view: VlcPlayerView, playInBackground: Boolean? ->
|
|
92
|
+
view.playInBackground = playInBackground ?: false
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
Prop("autoplay") { view: VlcPlayerView, autoplay: Boolean? ->
|
|
96
|
+
view.setAutoplay(autoplay ?: true)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
OnViewDestroys { view: VlcPlayerView ->
|
|
100
|
+
VlcPlayerManager.onViewDestroyed(view)
|
|
101
|
+
VlcPlayerManager.unregisterView(view)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
AsyncFunction("play") { view: VlcPlayerView ->
|
|
105
|
+
view.play()
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
AsyncFunction("pause") { view: VlcPlayerView ->
|
|
109
|
+
view.pause()
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
AsyncFunction("stop") { view: VlcPlayerView ->
|
|
113
|
+
view.stop()
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
AsyncFunction("seek") { view: VlcPlayerView, position: Float ->
|
|
117
|
+
view.seek(position)
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
OnActivityEntersForeground {
|
|
122
|
+
VlcPlayerManager.onAppForegrounded()
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
OnActivityEntersBackground {
|
|
126
|
+
VlcPlayerManager.onAppBackgrounded()
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|