expo-video-subtitle 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/CHANGELOG.md +34 -0
- package/FORK.md +149 -0
- package/LICENSE +21 -0
- package/README.md +108 -0
- package/android/build.gradle +32 -0
- package/android/src/main/AndroidManifest.xml +10 -0
- package/android/src/main/java/expo/modules/video/FullscreenPlayerActivity.kt +258 -0
- package/android/src/main/java/expo/modules/video/IntervalUpdateClock.kt +54 -0
- package/android/src/main/java/expo/modules/video/MediaMetadataRetriever.kt +89 -0
- package/android/src/main/java/expo/modules/video/PlayerViewExtension.kt +87 -0
- package/android/src/main/java/expo/modules/video/VideoCache.kt +109 -0
- package/android/src/main/java/expo/modules/video/VideoExceptions.kt +45 -0
- package/android/src/main/java/expo/modules/video/VideoModule.kt +446 -0
- package/android/src/main/java/expo/modules/video/VideoThumbnail.kt +20 -0
- package/android/src/main/java/expo/modules/video/VideoView.kt +457 -0
- package/android/src/main/java/expo/modules/video/delegates/IgnoreSameSet.kt +24 -0
- package/android/src/main/java/expo/modules/video/drawing/OutlineProvider.kt +217 -0
- package/android/src/main/java/expo/modules/video/enums/AudioMixingMode.kt +20 -0
- package/android/src/main/java/expo/modules/video/enums/ContentFit.kt +19 -0
- package/android/src/main/java/expo/modules/video/enums/ContentType.kt +22 -0
- package/android/src/main/java/expo/modules/video/enums/DRMType.kt +26 -0
- package/android/src/main/java/expo/modules/video/enums/FullscreenOrientation.kt +25 -0
- package/android/src/main/java/expo/modules/video/enums/PlayerStatus.kt +10 -0
- package/android/src/main/java/expo/modules/video/enums/VideoRange.kt +27 -0
- package/android/src/main/java/expo/modules/video/listeners/VideoManagerListener.kt +10 -0
- package/android/src/main/java/expo/modules/video/listeners/VideoPlayerListener.kt +35 -0
- package/android/src/main/java/expo/modules/video/listeners/VideoViewListener.kt +13 -0
- package/android/src/main/java/expo/modules/video/managers/AudioFocusManager.kt +242 -0
- package/android/src/main/java/expo/modules/video/managers/PictureInPictureManager.kt +329 -0
- package/android/src/main/java/expo/modules/video/managers/VideoManager.kt +194 -0
- package/android/src/main/java/expo/modules/video/playbackService/ExpoVideoPlaybackService.kt +267 -0
- package/android/src/main/java/expo/modules/video/playbackService/PlaybackServiceConnection.kt +62 -0
- package/android/src/main/java/expo/modules/video/playbackService/VideoMediaSessionCallback.kt +47 -0
- package/android/src/main/java/expo/modules/video/player/DefaultLoadControl.java +552 -0
- package/android/src/main/java/expo/modules/video/player/FirstFrameEventGenerator.kt +116 -0
- package/android/src/main/java/expo/modules/video/player/PlayerEvent.kt +172 -0
- package/android/src/main/java/expo/modules/video/player/VideoPlayer.kt +598 -0
- package/android/src/main/java/expo/modules/video/player/VideoPlayerAudioTracks.kt +126 -0
- package/android/src/main/java/expo/modules/video/player/VideoPlayerKeepAwake.kt +91 -0
- package/android/src/main/java/expo/modules/video/player/VideoPlayerLoadControl.kt +57 -0
- package/android/src/main/java/expo/modules/video/player/VideoPlayerSubtitles.kt +126 -0
- package/android/src/main/java/expo/modules/video/records/BufferOptions.kt +18 -0
- package/android/src/main/java/expo/modules/video/records/ButtonOptions.kt +17 -0
- package/android/src/main/java/expo/modules/video/records/DRMOptions.kt +27 -0
- package/android/src/main/java/expo/modules/video/records/FullscreenOptions.kt +14 -0
- package/android/src/main/java/expo/modules/video/records/PiPParams.kt +38 -0
- package/android/src/main/java/expo/modules/video/records/PlaybackError.kt +21 -0
- package/android/src/main/java/expo/modules/video/records/PlayerBuilderOptions.kt +15 -0
- package/android/src/main/java/expo/modules/video/records/ScrubbingModeOptions.kt +33 -0
- package/android/src/main/java/expo/modules/video/records/SeekTolerance.kt +30 -0
- package/android/src/main/java/expo/modules/video/records/SubtitleSource.kt +54 -0
- package/android/src/main/java/expo/modules/video/records/SubtitleStyle.kt +98 -0
- package/android/src/main/java/expo/modules/video/records/Tracks.kt +115 -0
- package/android/src/main/java/expo/modules/video/records/VideoEventPayloads.kt +79 -0
- package/android/src/main/java/expo/modules/video/records/VideoMetadata.kt +14 -0
- package/android/src/main/java/expo/modules/video/records/VideoSize.kt +16 -0
- package/android/src/main/java/expo/modules/video/records/VideoSource.kt +113 -0
- package/android/src/main/java/expo/modules/video/records/VideoThumbnailOptions.kt +26 -0
- package/android/src/main/java/expo/modules/video/utils/DataSourceUtils.kt +76 -0
- package/android/src/main/java/expo/modules/video/utils/EventDispatcherUtils.kt +43 -0
- package/android/src/main/java/expo/modules/video/utils/FullscreenActivityOrientationHelper.kt +120 -0
- package/android/src/main/java/expo/modules/video/utils/MediaSessionUtils.kt +14 -0
- package/android/src/main/java/expo/modules/video/utils/MutableWeakReference.kt +26 -0
- package/android/src/main/java/expo/modules/video/utils/PictureInPictureHelperFragment.kt +49 -0
- package/android/src/main/java/expo/modules/video/utils/PictureInPictureUtils.kt +96 -0
- package/android/src/main/java/expo/modules/video/utils/SubtitleUtils.kt +68 -0
- package/android/src/main/java/expo/modules/video/utils/ViewVisibilityUtils.kt +28 -0
- package/android/src/main/java/expo/modules/video/utils/WeakMutableSet.kt +120 -0
- package/android/src/main/java/expo/modules/video/utils/YogaUtils.kt +20 -0
- package/android/src/main/res/drawable/seek_backwards_10s.xml +25 -0
- package/android/src/main/res/drawable/seek_backwards_15s.xml +25 -0
- package/android/src/main/res/drawable/seek_backwards_5s.xml +25 -0
- package/android/src/main/res/drawable/seek_forwards_10s.xml +30 -0
- package/android/src/main/res/drawable/seek_forwards_15s.xml +31 -0
- package/android/src/main/res/drawable/seek_forwards_5s.xml +30 -0
- package/android/src/main/res/layout/fullscreen_player_activity.xml +20 -0
- package/android/src/main/res/layout/surface_player_view.xml +7 -0
- package/android/src/main/res/layout/texture_player_view.xml +7 -0
- package/android/src/main/res/values/styles.xml +9 -0
- package/app.plugin.js +1 -0
- package/build/NativeVideoAirPlayButtonView.d.ts +3 -0
- package/build/NativeVideoAirPlayButtonView.d.ts.map +1 -0
- package/build/NativeVideoAirPlayButtonView.js +3 -0
- package/build/NativeVideoAirPlayButtonView.js.map +1 -0
- package/build/NativeVideoModule.d.ts +13 -0
- package/build/NativeVideoModule.d.ts.map +1 -0
- package/build/NativeVideoModule.js +3 -0
- package/build/NativeVideoModule.js.map +1 -0
- package/build/NativeVideoModule.web.d.ts +5 -0
- package/build/NativeVideoModule.web.d.ts.map +1 -0
- package/build/NativeVideoModule.web.js +5 -0
- package/build/NativeVideoModule.web.js.map +1 -0
- package/build/NativeVideoView.d.ts +4 -0
- package/build/NativeVideoView.d.ts.map +1 -0
- package/build/NativeVideoView.js +6 -0
- package/build/NativeVideoView.js.map +1 -0
- package/build/VideoAirPlayButton.d.ts +10 -0
- package/build/VideoAirPlayButton.d.ts.map +1 -0
- package/build/VideoAirPlayButton.ios.d.ts +3 -0
- package/build/VideoAirPlayButton.ios.d.ts.map +1 -0
- package/build/VideoAirPlayButton.ios.js +6 -0
- package/build/VideoAirPlayButton.ios.js.map +1 -0
- package/build/VideoAirPlayButton.js +13 -0
- package/build/VideoAirPlayButton.js.map +1 -0
- package/build/VideoAirPlayButton.types.d.ts +35 -0
- package/build/VideoAirPlayButton.types.d.ts.map +1 -0
- package/build/VideoAirPlayButton.types.js +2 -0
- package/build/VideoAirPlayButton.types.js.map +1 -0
- package/build/VideoModule.d.ts +35 -0
- package/build/VideoModule.d.ts.map +1 -0
- package/build/VideoModule.js +44 -0
- package/build/VideoModule.js.map +1 -0
- package/build/VideoPlayer.d.ts +17 -0
- package/build/VideoPlayer.d.ts.map +1 -0
- package/build/VideoPlayer.js +55 -0
- package/build/VideoPlayer.js.map +1 -0
- package/build/VideoPlayer.types.d.ts +782 -0
- package/build/VideoPlayer.types.d.ts.map +1 -0
- package/build/VideoPlayer.types.js +2 -0
- package/build/VideoPlayer.types.js.map +1 -0
- package/build/VideoPlayer.web.d.ts +79 -0
- package/build/VideoPlayer.web.d.ts.map +1 -0
- package/build/VideoPlayer.web.js +388 -0
- package/build/VideoPlayer.web.js.map +1 -0
- package/build/VideoPlayerEvents.types.d.ts +278 -0
- package/build/VideoPlayerEvents.types.d.ts.map +1 -0
- package/build/VideoPlayerEvents.types.js +2 -0
- package/build/VideoPlayerEvents.types.js.map +1 -0
- package/build/VideoThumbnail.d.ts +29 -0
- package/build/VideoThumbnail.d.ts.map +1 -0
- package/build/VideoThumbnail.js +4 -0
- package/build/VideoThumbnail.js.map +1 -0
- package/build/VideoView.d.ts +69 -0
- package/build/VideoView.d.ts.map +1 -0
- package/build/VideoView.js +100 -0
- package/build/VideoView.js.map +1 -0
- package/build/VideoView.types.d.ts +362 -0
- package/build/VideoView.types.d.ts.map +1 -0
- package/build/VideoView.types.js +2 -0
- package/build/VideoView.types.js.map +1 -0
- package/build/VideoView.web.d.ts +9 -0
- package/build/VideoView.web.d.ts.map +1 -0
- package/build/VideoView.web.js +239 -0
- package/build/VideoView.web.js.map +1 -0
- package/build/index.d.ts +11 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +6 -0
- package/build/index.js.map +1 -0
- package/build/resolveAssetSource.d.ts +3 -0
- package/build/resolveAssetSource.d.ts.map +1 -0
- package/build/resolveAssetSource.js +3 -0
- package/build/resolveAssetSource.js.map +1 -0
- package/build/resolveAssetSource.web.d.ts +4 -0
- package/build/resolveAssetSource.web.d.ts.map +1 -0
- package/build/resolveAssetSource.web.js +16 -0
- package/build/resolveAssetSource.web.js.map +1 -0
- package/expo-module.config.json +9 -0
- package/ios/Cache/CachableRequest.swift +46 -0
- package/ios/Cache/CacheVideoAssetTransportProvider.swift +64 -0
- package/ios/Cache/CachedResource.swift +164 -0
- package/ios/Cache/CachingHelpers.swift +107 -0
- package/ios/Cache/MediaFileHandle.swift +139 -0
- package/ios/Cache/MediaInfo.swift +185 -0
- package/ios/Cache/ResourceLoaderDelegate.swift +306 -0
- package/ios/Cache/SynchronizedHashTable.swift +23 -0
- package/ios/Cache/VideoCacheManager.swift +192 -0
- package/ios/ContentKeyDelegate.swift +214 -0
- package/ios/ContentKeyManager.swift +21 -0
- package/ios/Enums/AudioMixingMode.swift +37 -0
- package/ios/Enums/ContentType.swift +11 -0
- package/ios/Enums/DRMType.swift +20 -0
- package/ios/Enums/FullscreenOrientation.swift +34 -0
- package/ios/Enums/KeepFullscreenOnPiPStopBehavior.swift +20 -0
- package/ios/Enums/PlayerStatus.swift +10 -0
- package/ios/Enums/VideoContentFit.swift +39 -0
- package/ios/Enums/VideoRange.swift +21 -0
- package/ios/ExpoVideo.podspec +27 -0
- package/ios/NowPlayingManager.swift +304 -0
- package/ios/OrientationAVPlayerViewController.swift +263 -0
- package/ios/Records/BufferOptions.swift +12 -0
- package/ios/Records/DRMOptions.swift +24 -0
- package/ios/Records/FullscreenOptions.swift +14 -0
- package/ios/Records/PlaybackError.swift +10 -0
- package/ios/Records/ScrubbingModeOptions.swift +7 -0
- package/ios/Records/SeekTolerance.swift +20 -0
- package/ios/Records/SubtitleSource.swift +32 -0
- package/ios/Records/SubtitleStyle.swift +118 -0
- package/ios/Records/Tracks.swift +229 -0
- package/ios/Records/VideoEventPayloads.swift +81 -0
- package/ios/Records/VideoMetadata.swift +16 -0
- package/ios/Records/VideoSize.swift +15 -0
- package/ios/Records/VideoSource.swift +28 -0
- package/ios/Thumbnails/VideoThumbnail.swift +27 -0
- package/ios/Thumbnails/VideoThumbnailGenerator.swift +68 -0
- package/ios/Thumbnails/VideoThumbnailOptions.swift +15 -0
- package/ios/Utils/AVAssetVariant+VideoTracks.swift +20 -0
- package/ios/Utils/AvAssetTrack+VideoTracks.swift +79 -0
- package/ios/Utils/FourCharCode+toString.swift +25 -0
- package/ios/Utils/HlsUriUtils.swift +27 -0
- package/ios/Utils/URL+MediaLibraryAssets.swift +68 -0
- package/ios/VideoAirPlayButtonView.swift +70 -0
- package/ios/VideoAsset.swift +98 -0
- package/ios/VideoAssetTransport/VideoAssetLoadPlan.swift +69 -0
- package/ios/VideoAssetTransport/VideoAssetSourceDescriptor.swift +29 -0
- package/ios/VideoAssetTransport/VideoAssetTransportProvider.swift +24 -0
- package/ios/VideoAssetTransport/VideoAssetTransportRegistry.swift +103 -0
- package/ios/VideoExceptions.swift +53 -0
- package/ios/VideoItem.swift +11 -0
- package/ios/VideoManager.swift +182 -0
- package/ios/VideoModule.swift +413 -0
- package/ios/VideoPlayer/DangerousPropertiesStore.swift +19 -0
- package/ios/VideoPlayer.swift +485 -0
- package/ios/VideoPlayerAudioTracks.swift +109 -0
- package/ios/VideoPlayerItem.swift +169 -0
- package/ios/VideoPlayerObserver.swift +612 -0
- package/ios/VideoPlayerSeeker.swift +77 -0
- package/ios/VideoPlayerSubtitleSideload.swift +203 -0
- package/ios/VideoPlayerSubtitles.swift +88 -0
- package/ios/VideoSourceLoader.swift +90 -0
- package/ios/VideoSourceLoaderListener.swift +34 -0
- package/ios/VideoView.swift +201 -0
- package/package.json +51 -0
- package/plugin/build/index.d.ts +3 -0
- package/plugin/build/index.js +3 -0
- package/plugin/build/withExpoVideo.d.ts +9 -0
- package/plugin/build/withExpoVideo.js +73 -0
- package/plugin/index.d.ts +1 -0
- package/plugin/index.js +1 -0
- package/plugin/jest.config.js +1 -0
- package/plugin/src/index.ts +3 -0
- package/plugin/src/withExpoVideo.ts +103 -0
- package/plugin/tsconfig.json +10 -0
- package/src/NativeVideoAirPlayButtonView.ts +3 -0
- package/src/NativeVideoModule.ts +16 -0
- package/src/NativeVideoModule.web.ts +4 -0
- package/src/NativeVideoView.ts +8 -0
- package/src/VideoAirPlayButton.ios.tsx +8 -0
- package/src/VideoAirPlayButton.tsx +14 -0
- package/src/VideoAirPlayButton.types.ts +39 -0
- package/src/VideoModule.ts +47 -0
- package/src/VideoPlayer.tsx +74 -0
- package/src/VideoPlayer.types.ts +893 -0
- package/src/VideoPlayer.web.tsx +466 -0
- package/src/VideoPlayerEvents.types.ts +332 -0
- package/src/VideoThumbnail.ts +31 -0
- package/src/VideoView.tsx +111 -0
- package/src/VideoView.types.ts +405 -0
- package/src/VideoView.web.tsx +293 -0
- package/src/index.ts +26 -0
- package/src/resolveAssetSource.ts +2 -0
- package/src/resolveAssetSource.web.ts +17 -0
- package/src/ts-declarations/react-native-assets.d.ts +22 -0
- package/tsconfig.all.json +11 -0
- package/tsconfig.json +10 -0
- package/tsconfig.tsbuildinfo +1 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
package expo.modules.video.enums
|
|
2
|
+
|
|
3
|
+
import expo.modules.kotlin.types.Enumerable
|
|
4
|
+
|
|
5
|
+
enum class AudioMixingMode(val value: String) : Enumerable {
|
|
6
|
+
MIX_WITH_OTHERS("mixWithOthers"),
|
|
7
|
+
DUCK_OTHERS("duckOthers"),
|
|
8
|
+
AUTO("auto"),
|
|
9
|
+
DO_NOT_MIX("doNotMix");
|
|
10
|
+
|
|
11
|
+
val priority: Int
|
|
12
|
+
get() {
|
|
13
|
+
return when (this) {
|
|
14
|
+
DO_NOT_MIX -> 3
|
|
15
|
+
AUTO -> 2
|
|
16
|
+
DUCK_OTHERS -> 1
|
|
17
|
+
MIX_WITH_OTHERS -> 0
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
package expo.modules.video.enums
|
|
2
|
+
|
|
3
|
+
import androidx.media3.ui.AspectRatioFrameLayout
|
|
4
|
+
import expo.modules.kotlin.types.Enumerable
|
|
5
|
+
|
|
6
|
+
@androidx.annotation.OptIn(androidx.media3.common.util.UnstableApi::class)
|
|
7
|
+
enum class ContentFit(val value: String) : Enumerable {
|
|
8
|
+
CONTAIN("contain"),
|
|
9
|
+
FILL("fill"),
|
|
10
|
+
COVER("cover");
|
|
11
|
+
|
|
12
|
+
fun toResizeMode(): Int {
|
|
13
|
+
return when (this) {
|
|
14
|
+
CONTAIN -> AspectRatioFrameLayout.RESIZE_MODE_FIT
|
|
15
|
+
FILL -> AspectRatioFrameLayout.RESIZE_MODE_FILL
|
|
16
|
+
COVER -> AspectRatioFrameLayout.RESIZE_MODE_ZOOM
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
package expo.modules.video.enums
|
|
2
|
+
|
|
3
|
+
import androidx.media3.common.MimeTypes
|
|
4
|
+
import expo.modules.kotlin.types.Enumerable
|
|
5
|
+
|
|
6
|
+
enum class ContentType(val value: String) : Enumerable {
|
|
7
|
+
AUTO("auto"),
|
|
8
|
+
PROGRESSIVE("progressive"),
|
|
9
|
+
HLS("hls"),
|
|
10
|
+
DASH("dash"),
|
|
11
|
+
SMOOTH_STREAMING("smoothStreaming");
|
|
12
|
+
|
|
13
|
+
fun toMimeTypeString(): String? {
|
|
14
|
+
return when (this) {
|
|
15
|
+
AUTO -> null
|
|
16
|
+
PROGRESSIVE -> null
|
|
17
|
+
HLS -> MimeTypes.APPLICATION_M3U8
|
|
18
|
+
DASH -> MimeTypes.APPLICATION_MPD
|
|
19
|
+
SMOOTH_STREAMING -> MimeTypes.APPLICATION_SS
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
package expo.modules.video.enums
|
|
2
|
+
|
|
3
|
+
import androidx.media3.common.C
|
|
4
|
+
import expo.modules.kotlin.types.Enumerable
|
|
5
|
+
import expo.modules.video.UnsupportedDRMTypeException
|
|
6
|
+
import java.util.UUID
|
|
7
|
+
|
|
8
|
+
enum class DRMType(val value: String) : Enumerable {
|
|
9
|
+
CLEARKEY("clearkey"),
|
|
10
|
+
FAIRPLAY("fairplay"),
|
|
11
|
+
PLAYREADY("playready"),
|
|
12
|
+
WIDEVINE("widevine");
|
|
13
|
+
|
|
14
|
+
fun isSupported(): Boolean {
|
|
15
|
+
return this != FAIRPLAY
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
fun toUUID(): UUID {
|
|
19
|
+
return when (this) {
|
|
20
|
+
CLEARKEY -> C.CLEARKEY_UUID
|
|
21
|
+
FAIRPLAY -> throw UnsupportedDRMTypeException(this)
|
|
22
|
+
PLAYREADY -> C.PLAYREADY_UUID
|
|
23
|
+
WIDEVINE -> C.WIDEVINE_UUID
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
package expo.modules.video.enums
|
|
2
|
+
import expo.modules.kotlin.types.Enumerable
|
|
3
|
+
import android.content.pm.ActivityInfo
|
|
4
|
+
|
|
5
|
+
enum class FullscreenOrientation(val value: String) : Enumerable {
|
|
6
|
+
LANDSCAPE("landscape"),
|
|
7
|
+
PORTRAIT("portrait"),
|
|
8
|
+
LANDSCAPE_LEFT("landscapeLeft"),
|
|
9
|
+
LANDSCAPE_RIGHT("landscapeRight"),
|
|
10
|
+
PORTRAIT_UP("portraitUp"),
|
|
11
|
+
PORTRAIT_DOWN("portraitDown"),
|
|
12
|
+
DEFAULT("default");
|
|
13
|
+
|
|
14
|
+
fun toActivityOrientation(): Int {
|
|
15
|
+
return when (this) {
|
|
16
|
+
LANDSCAPE -> ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE
|
|
17
|
+
PORTRAIT -> ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
|
|
18
|
+
LANDSCAPE_LEFT -> ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
|
|
19
|
+
LANDSCAPE_RIGHT -> ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE
|
|
20
|
+
PORTRAIT_UP -> ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
|
|
21
|
+
PORTRAIT_DOWN -> ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT
|
|
22
|
+
DEFAULT -> ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
package expo.modules.video.records
|
|
2
|
+
|
|
3
|
+
import androidx.annotation.OptIn
|
|
4
|
+
import androidx.media3.common.C
|
|
5
|
+
import androidx.media3.common.C.COLOR_TRANSFER_HLG
|
|
6
|
+
import androidx.media3.common.C.COLOR_TRANSFER_ST2084
|
|
7
|
+
import androidx.media3.common.util.UnstableApi
|
|
8
|
+
import expo.modules.kotlin.types.Enumerable
|
|
9
|
+
|
|
10
|
+
enum class VideoRange(val value: String) : Enumerable {
|
|
11
|
+
SDR("sdr"),
|
|
12
|
+
HLG("hlg"),
|
|
13
|
+
PQ("PQ");
|
|
14
|
+
|
|
15
|
+
companion object {
|
|
16
|
+
@OptIn(UnstableApi::class)
|
|
17
|
+
fun fromCColorTransfer(colorTransfer: @C.ColorTransfer Int?): VideoRange {
|
|
18
|
+
colorTransfer ?: return SDR
|
|
19
|
+
|
|
20
|
+
return when (colorTransfer) {
|
|
21
|
+
COLOR_TRANSFER_ST2084 -> PQ
|
|
22
|
+
COLOR_TRANSFER_HLG -> HLG
|
|
23
|
+
else -> SDR
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
package expo.modules.video.listeners
|
|
2
|
+
|
|
3
|
+
import expo.modules.video.VideoView
|
|
4
|
+
|
|
5
|
+
interface VideoManagerListener {
|
|
6
|
+
fun onVideoViewRegistered(videoView: VideoView, allVideoViews: Collection<VideoView>) {}
|
|
7
|
+
fun onVideoViewUnregistered(videoView: VideoView, allVideoViews: Collection<VideoView>) {}
|
|
8
|
+
fun onAppBackgrounded() {}
|
|
9
|
+
fun onAppForegrounded() {}
|
|
10
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
package expo.modules.video.listeners
|
|
2
|
+
|
|
3
|
+
import androidx.annotation.OptIn
|
|
4
|
+
import androidx.media3.common.TrackSelectionParameters
|
|
5
|
+
import androidx.media3.common.Tracks
|
|
6
|
+
import androidx.media3.common.util.UnstableApi
|
|
7
|
+
import expo.modules.video.VideoView
|
|
8
|
+
import expo.modules.video.enums.AudioMixingMode
|
|
9
|
+
import expo.modules.video.enums.PlayerStatus
|
|
10
|
+
import expo.modules.video.player.VideoPlayer
|
|
11
|
+
import expo.modules.video.records.AudioTrack
|
|
12
|
+
import expo.modules.video.records.PlaybackError
|
|
13
|
+
import expo.modules.video.records.SubtitleTrack
|
|
14
|
+
import expo.modules.video.records.TimeUpdate
|
|
15
|
+
import expo.modules.video.records.VideoSource
|
|
16
|
+
import expo.modules.video.records.VideoTrack
|
|
17
|
+
|
|
18
|
+
@OptIn(UnstableApi::class)
|
|
19
|
+
interface VideoPlayerListener {
|
|
20
|
+
fun onStatusChanged(player: VideoPlayer, status: PlayerStatus, oldStatus: PlayerStatus?, error: PlaybackError?) {}
|
|
21
|
+
fun onIsPlayingChanged(player: VideoPlayer, isPlaying: Boolean, oldIsPlaying: Boolean?) {}
|
|
22
|
+
fun onVolumeChanged(player: VideoPlayer, volume: Float, oldVolume: Float?) {}
|
|
23
|
+
fun onMutedChanged(player: VideoPlayer, muted: Boolean, oldMuted: Boolean?) {}
|
|
24
|
+
fun onSourceChanged(player: VideoPlayer, source: VideoSource?, oldSource: VideoSource?) {}
|
|
25
|
+
fun onPlaybackRateChanged(player: VideoPlayer, rate: Float, oldRate: Float?) {}
|
|
26
|
+
fun onTracksChanged(player: VideoPlayer, tracks: Tracks) {}
|
|
27
|
+
fun onTrackSelectionParametersChanged(player: VideoPlayer, trackSelectionParameters: TrackSelectionParameters) {}
|
|
28
|
+
fun onTimeUpdate(player: VideoPlayer, timeUpdate: TimeUpdate) {}
|
|
29
|
+
fun onPlayedToEnd(player: VideoPlayer) {}
|
|
30
|
+
fun onAudioMixingModeChanged(player: VideoPlayer, audioMixingMode: AudioMixingMode, oldAudioMixingMode: AudioMixingMode?) {}
|
|
31
|
+
fun onVideoTrackChanged(player: VideoPlayer, videoTrack: VideoTrack?, oldVideoTrack: VideoTrack?) {}
|
|
32
|
+
fun onVideoSourceLoaded(player: VideoPlayer, videoSource: VideoSource?, duration: Double?, availableVideoTracks: List<VideoTrack>, availableSubtitleTracks: List<SubtitleTrack>, availableAudioTracks: List<AudioTrack>) {}
|
|
33
|
+
fun onTargetViewChanged(player: VideoPlayer, newTargetView: VideoView?, oldTargetView: VideoView?) {}
|
|
34
|
+
fun onRenderedFirstFrame(player: VideoPlayer) {}
|
|
35
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
package expo.modules.video.listeners
|
|
2
|
+
|
|
3
|
+
import expo.modules.video.VideoView
|
|
4
|
+
import expo.modules.video.records.PiPParams
|
|
5
|
+
|
|
6
|
+
interface VideoViewListener {
|
|
7
|
+
/**
|
|
8
|
+
* Called when the ability of the view to autoEnterPip has changed.
|
|
9
|
+
* Note: This can be called when `autoEnterPiP` doesn't change, but other factors
|
|
10
|
+
* influence the view's ability to enter PiP.
|
|
11
|
+
*/
|
|
12
|
+
fun onPiPParamsChanged(videoView: VideoView, oldPiPParams: PiPParams, newPiPParams: PiPParams) = Unit
|
|
13
|
+
}
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
package expo.modules.video.managers
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.media.AudioAttributes
|
|
5
|
+
import android.media.AudioFocusRequest
|
|
6
|
+
import android.media.AudioManager
|
|
7
|
+
import android.os.Build
|
|
8
|
+
import androidx.media3.common.util.UnstableApi
|
|
9
|
+
import expo.modules.kotlin.AppContext
|
|
10
|
+
import expo.modules.video.FailedToGetAudioFocusManagerException
|
|
11
|
+
import expo.modules.video.enums.AudioMixingMode
|
|
12
|
+
import expo.modules.video.player.VideoPlayer
|
|
13
|
+
import expo.modules.video.listeners.VideoPlayerListener
|
|
14
|
+
import kotlinx.coroutines.launch
|
|
15
|
+
import java.lang.ref.WeakReference
|
|
16
|
+
|
|
17
|
+
@UnstableApi
|
|
18
|
+
class AudioFocusManager(private val appContext: AppContext) : AudioManager.OnAudioFocusChangeListener, VideoPlayerListener {
|
|
19
|
+
private val audioManager by lazy {
|
|
20
|
+
appContext.reactContext?.getSystemService(Context.AUDIO_SERVICE) as? AudioManager ?: run {
|
|
21
|
+
throw FailedToGetAudioFocusManagerException()
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
private var players: MutableList<WeakReference<VideoPlayer>> = mutableListOf()
|
|
26
|
+
private var currentFocusRequest: AudioFocusRequest? = null
|
|
27
|
+
private var currentMixingMode: AudioMixingMode = AudioMixingMode.MIX_WITH_OTHERS
|
|
28
|
+
private val anyPlayerRequiresFocus: Boolean
|
|
29
|
+
get() = players.toList().any {
|
|
30
|
+
playerRequiresFocus(it)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
private fun requestAudioFocus() {
|
|
34
|
+
val audioMixingMode = findAudioMixingMode()
|
|
35
|
+
|
|
36
|
+
// We don't request AudioFocus if we want to mix the audio with others
|
|
37
|
+
if (audioMixingMode == AudioMixingMode.MIX_WITH_OTHERS || !anyPlayerRequiresFocus) {
|
|
38
|
+
abandonAudioFocus()
|
|
39
|
+
currentMixingMode = audioMixingMode
|
|
40
|
+
return
|
|
41
|
+
}
|
|
42
|
+
val audioFocusType = when (audioMixingMode) {
|
|
43
|
+
AudioMixingMode.DUCK_OTHERS -> AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK
|
|
44
|
+
AudioMixingMode.AUTO -> AudioManager.AUDIOFOCUS_GAIN
|
|
45
|
+
AudioMixingMode.DO_NOT_MIX -> AudioManager.AUDIOFOCUS_GAIN
|
|
46
|
+
else -> AudioManager.AUDIOFOCUS_GAIN
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
50
|
+
// We already have audio focus
|
|
51
|
+
currentFocusRequest?.let {
|
|
52
|
+
if (it.focusGain == audioFocusType) {
|
|
53
|
+
return
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
val newFocusRequest = AudioFocusRequest.Builder(audioFocusType).run {
|
|
58
|
+
setAudioAttributes(
|
|
59
|
+
AudioAttributes.Builder().run {
|
|
60
|
+
setUsage(AudioAttributes.USAGE_MEDIA)
|
|
61
|
+
setContentType(AudioAttributes.CONTENT_TYPE_MOVIE)
|
|
62
|
+
setOnAudioFocusChangeListener(this@AudioFocusManager)
|
|
63
|
+
build()
|
|
64
|
+
}
|
|
65
|
+
).build()
|
|
66
|
+
}
|
|
67
|
+
currentFocusRequest = newFocusRequest
|
|
68
|
+
audioManager.requestAudioFocus(newFocusRequest)
|
|
69
|
+
} else {
|
|
70
|
+
@Suppress("DEPRECATION")
|
|
71
|
+
audioManager.requestAudioFocus(
|
|
72
|
+
this,
|
|
73
|
+
AudioManager.STREAM_MUSIC,
|
|
74
|
+
audioFocusType
|
|
75
|
+
)
|
|
76
|
+
}
|
|
77
|
+
currentMixingMode = audioMixingMode
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
private fun abandonAudioFocus() {
|
|
81
|
+
currentFocusRequest?.let {
|
|
82
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
83
|
+
audioManager.abandonAudioFocusRequest(it)
|
|
84
|
+
} else {
|
|
85
|
+
@Suppress("DEPRECATION")
|
|
86
|
+
audioManager.abandonAudioFocus(this)
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
currentFocusRequest = null
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
fun registerPlayer(player: VideoPlayer) {
|
|
93
|
+
players.find { it.get() == player } ?: run {
|
|
94
|
+
players.add(WeakReference(player))
|
|
95
|
+
}
|
|
96
|
+
player.addListener(this)
|
|
97
|
+
updateAudioFocus()
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
fun unregisterPlayer(player: VideoPlayer) {
|
|
101
|
+
player.removeListener(this)
|
|
102
|
+
players.removeAll { it.get() == player }
|
|
103
|
+
updateAudioFocus()
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// VideoPlayerListener
|
|
107
|
+
|
|
108
|
+
override fun onAudioMixingModeChanged(player: VideoPlayer, audioMixingMode: AudioMixingMode, oldAudioMixingMode: AudioMixingMode?) {
|
|
109
|
+
requestAudioFocus()
|
|
110
|
+
super.onAudioMixingModeChanged(player, audioMixingMode, oldAudioMixingMode)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
override fun onIsPlayingChanged(player: VideoPlayer, isPlaying: Boolean, oldIsPlaying: Boolean?) {
|
|
114
|
+
// we can't use `updateAudioFocus`, because when losing focus the videos are paused sequentially,
|
|
115
|
+
// which can lead to unexpected results.
|
|
116
|
+
if (!isPlaying && !anyPlayerRequiresFocus) {
|
|
117
|
+
abandonAudioFocus()
|
|
118
|
+
} else if (isPlaying && anyPlayerRequiresFocus) {
|
|
119
|
+
requestAudioFocus()
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
override fun onVolumeChanged(player: VideoPlayer, volume: Float, oldVolume: Float?) {
|
|
124
|
+
updateAudioFocus()
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
override fun onMutedChanged(player: VideoPlayer, muted: Boolean, oldMuted: Boolean?) {
|
|
128
|
+
updateAudioFocus()
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// AudioManager.OnAudioFocusChangeListener
|
|
132
|
+
|
|
133
|
+
override fun onAudioFocusChange(focusChange: Int) {
|
|
134
|
+
when (focusChange) {
|
|
135
|
+
AudioManager.AUDIOFOCUS_LOSS -> {
|
|
136
|
+
appContext.mainQueue.launch {
|
|
137
|
+
players.forEach {
|
|
138
|
+
pausePlayerIfUnmuted(it)
|
|
139
|
+
}
|
|
140
|
+
currentFocusRequest = null
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
AudioManager.AUDIOFOCUS_LOSS_TRANSIENT -> {
|
|
145
|
+
// W could pause/mix the players here individually, but we will keep the behaviour in line with iOS,
|
|
146
|
+
// find the dominant audioMixingMode and apply it to all players.
|
|
147
|
+
val audioMixingMode = findAudioMixingMode()
|
|
148
|
+
if (audioMixingMode == AudioMixingMode.MIX_WITH_OTHERS) {
|
|
149
|
+
return
|
|
150
|
+
}
|
|
151
|
+
appContext.mainQueue.launch {
|
|
152
|
+
players.forEach {
|
|
153
|
+
pausePlayerIfUnmuted(it)
|
|
154
|
+
}
|
|
155
|
+
currentFocusRequest = null
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK -> {
|
|
160
|
+
val audioMixingMode = findAudioMixingMode()
|
|
161
|
+
|
|
162
|
+
appContext.mainQueue.launch {
|
|
163
|
+
players.forEach {
|
|
164
|
+
if (audioMixingMode == AudioMixingMode.DO_NOT_MIX) {
|
|
165
|
+
pausePlayerIfUnmuted(it)
|
|
166
|
+
} else {
|
|
167
|
+
duckPlayer(it)
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
AudioManager.AUDIOFOCUS_GAIN -> {
|
|
174
|
+
// TODO: For now this behaves like iOS and doesn't resume playback automatically
|
|
175
|
+
// In future versions we can add a prop to control this behavior.
|
|
176
|
+
appContext.mainQueue.launch {
|
|
177
|
+
players.forEach {
|
|
178
|
+
unduckPlayer(it)
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Utils
|
|
186
|
+
|
|
187
|
+
private fun playerRequiresFocus(weakPlayer: WeakReference<VideoPlayer>): Boolean {
|
|
188
|
+
val player = weakPlayer?.get() ?: return false // Return false if player is null
|
|
189
|
+
return (!player.muted && player.playing && player.volume > 0) || player.audioMixingMode == AudioMixingMode.DO_NOT_MIX
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
private fun pausePlayerIfUnmuted(weakPlayer: WeakReference<VideoPlayer>) {
|
|
193
|
+
weakPlayer.get()?.let { videoPlayer ->
|
|
194
|
+
if (!videoPlayer.muted) {
|
|
195
|
+
appContext.mainQueue.launch {
|
|
196
|
+
videoPlayer.player.pause()
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
private fun duckPlayer(weakPlayer: WeakReference<VideoPlayer>) {
|
|
203
|
+
weakPlayer.get()?.let { player ->
|
|
204
|
+
appContext.mainQueue.launch {
|
|
205
|
+
player.volume /= 2f
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
private fun unduckPlayer(weakPlayer: WeakReference<VideoPlayer>) {
|
|
211
|
+
weakPlayer.get()?.let { player ->
|
|
212
|
+
if (!player.muted) {
|
|
213
|
+
appContext.mainQueue.launch {
|
|
214
|
+
player.volume = player.userVolume
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
private fun updateAudioFocus() {
|
|
221
|
+
if (anyPlayerRequiresFocus || findAudioMixingMode() != currentMixingMode) {
|
|
222
|
+
requestAudioFocus()
|
|
223
|
+
} else {
|
|
224
|
+
abandonAudioFocus()
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
private fun findAudioMixingMode(): AudioMixingMode {
|
|
229
|
+
val playersSnapshot = players.toList()
|
|
230
|
+
|
|
231
|
+
val mixingModes = playersSnapshot.mapNotNull { player ->
|
|
232
|
+
player.get()?.takeIf { it.playing }?.audioMixingMode
|
|
233
|
+
}
|
|
234
|
+
if (mixingModes.isEmpty()) {
|
|
235
|
+
return AudioMixingMode.MIX_WITH_OTHERS
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return mixingModes.reduce { currentAudioMixingMode, next ->
|
|
239
|
+
next.takeIf { it.priority > currentAudioMixingMode.priority } ?: currentAudioMixingMode
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|