expo-video 1.1.4 → 1.1.6
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 +16 -0
- package/android/build.gradle +2 -2
- package/android/src/main/java/expo/modules/video/ExpoVideoPlaybackService.kt +17 -1
- package/android/src/main/java/expo/modules/video/VideoExceptions.kt +3 -0
- package/android/src/main/java/expo/modules/video/VideoModule.kt +12 -3
- package/android/src/main/java/expo/modules/video/VideoPlayer.kt +33 -15
- package/android/src/main/java/expo/modules/video/VideoPlayerAudioFocusManager.kt +103 -0
- package/android/src/main/java/expo/modules/video/records/VideoMetadata.kt +10 -0
- package/android/src/main/java/expo/modules/video/records/VideoSource.kt +15 -2
- package/android/src/main/java/expo/modules/video/records/VolumeEvent.kt +1 -1
- package/build/NativeVideoModule.d.ts +6 -4
- package/build/NativeVideoModule.d.ts.map +1 -1
- package/build/NativeVideoModule.js +0 -3
- package/build/NativeVideoModule.js.map +1 -1
- package/build/VideoPlayer.d.ts +1 -1
- package/build/VideoPlayer.d.ts.map +1 -1
- package/build/VideoPlayer.js.map +1 -1
- package/build/VideoPlayer.web.d.ts +2 -1
- package/build/VideoPlayer.web.d.ts.map +1 -1
- package/build/VideoPlayer.web.js +1 -0
- package/build/VideoPlayer.web.js.map +1 -1
- package/build/VideoView.d.ts +2 -2
- package/build/VideoView.d.ts.map +1 -1
- package/build/VideoView.js.map +1 -1
- package/build/VideoView.types.d.ts +47 -8
- package/build/VideoView.types.d.ts.map +1 -1
- package/build/VideoView.types.js.map +1 -1
- package/build/VideoView.web.d.ts +1 -1
- package/build/VideoView.web.d.ts.map +1 -1
- package/build/VideoView.web.js.map +1 -1
- package/ios/NowPlayingManager.swift +54 -35
- package/ios/Records/VideoMetadata.swift +13 -0
- package/ios/Records/VideoSource.swift +8 -3
- package/ios/VideoManager.swift +7 -3
- package/ios/VideoModule.swift +8 -1
- package/ios/VideoPlayer.swift +31 -11
- package/ios/VideoPlayerObserver.swift +86 -13
- package/package.json +2 -2
- package/src/NativeVideoModule.ts +8 -4
- package/src/VideoPlayer.tsx +1 -1
- package/src/VideoPlayer.web.tsx +7 -1
- package/src/VideoView.tsx +2 -2
- package/src/VideoView.types.ts +58 -10
- package/src/VideoView.web.tsx +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,22 @@
|
|
|
10
10
|
|
|
11
11
|
### 💡 Others
|
|
12
12
|
|
|
13
|
+
## 1.1.6 — 2024-05-01
|
|
14
|
+
|
|
15
|
+
### 🎉 New features
|
|
16
|
+
|
|
17
|
+
- [Android] Add support for customizing the now playing notification. ([#28390](https://github.com/expo/expo/pull/28390) by [@behenate](https://github.com/behenate))
|
|
18
|
+
|
|
19
|
+
### 💡 Others
|
|
20
|
+
|
|
21
|
+
- [Android] Improve audio focus management. ([#28453](https://github.com/expo/expo/pull/28453) by [@behenate](https://github.com/behenate))
|
|
22
|
+
|
|
23
|
+
## 1.1.5 — 2024-04-26
|
|
24
|
+
|
|
25
|
+
### 🎉 New features
|
|
26
|
+
|
|
27
|
+
- [iOS] Add support for customizing the now playing notification. ([#28386](https://github.com/expo/expo/pull/28386) by [@behenate](https://github.com/behenate))
|
|
28
|
+
|
|
13
29
|
## 1.1.4 — 2024-04-25
|
|
14
30
|
|
|
15
31
|
_This version does not introduce any user-facing changes._
|
package/android/build.gradle
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
apply plugin: 'com.android.library'
|
|
2
2
|
|
|
3
3
|
group = 'host.exp.exponent'
|
|
4
|
-
version = '1.1.
|
|
4
|
+
version = '1.1.6'
|
|
5
5
|
|
|
6
6
|
def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
|
|
7
7
|
apply from: expoModulesCorePlugin
|
|
@@ -14,7 +14,7 @@ android {
|
|
|
14
14
|
namespace "expo.modules.video"
|
|
15
15
|
defaultConfig {
|
|
16
16
|
versionCode 1
|
|
17
|
-
versionName '1.1.
|
|
17
|
+
versionName '1.1.6'
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
20
|
|
|
@@ -40,6 +40,15 @@ class ExpoVideoPlaybackService : MediaSessionService() {
|
|
|
40
40
|
.setIconResId(R.drawable.seek_backwards_10s)
|
|
41
41
|
.build()
|
|
42
42
|
|
|
43
|
+
fun setShowNotification(showNotification: Boolean, player: ExoPlayer) {
|
|
44
|
+
val sessionExtras = mediaSessions[player]?.sessionExtras?.deepCopy() ?: Bundle()
|
|
45
|
+
sessionExtras.putBoolean(SESSION_SHOW_NOTIFICATION, showNotification)
|
|
46
|
+
mediaSessions[player]?.let {
|
|
47
|
+
it.sessionExtras = sessionExtras
|
|
48
|
+
onUpdateNotification(it, showNotification)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
43
52
|
fun registerPlayer(player: ExoPlayer) {
|
|
44
53
|
if (mediaSessions[player] != null) {
|
|
45
54
|
return
|
|
@@ -71,7 +80,13 @@ class ExpoVideoPlaybackService : MediaSessionService() {
|
|
|
71
80
|
}
|
|
72
81
|
|
|
73
82
|
override fun onUpdateNotification(session: MediaSession, startInForegroundRequired: Boolean) {
|
|
74
|
-
|
|
83
|
+
if (session.sessionExtras.getBoolean(SESSION_SHOW_NOTIFICATION, true)) {
|
|
84
|
+
createNotification(session)
|
|
85
|
+
} else {
|
|
86
|
+
(session.player as? ExoPlayer)?.let {
|
|
87
|
+
hidePlayerNotification(it)
|
|
88
|
+
}
|
|
89
|
+
}
|
|
75
90
|
}
|
|
76
91
|
|
|
77
92
|
override fun onTaskRemoved(rootIntent: Intent?) {
|
|
@@ -135,6 +150,7 @@ class ExpoVideoPlaybackService : MediaSessionService() {
|
|
|
135
150
|
const val SEEK_FORWARD_COMMAND = "SEEK_FORWARD"
|
|
136
151
|
const val SEEK_BACKWARD_COMMAND = "SEEK_REWIND"
|
|
137
152
|
const val CHANNEL_ID = "PlaybackService"
|
|
153
|
+
const val SESSION_SHOW_NOTIFICATION = "showNotification"
|
|
138
154
|
const val SEEK_INTERVAL_MS = 10000L
|
|
139
155
|
}
|
|
140
156
|
}
|
|
@@ -23,3 +23,6 @@ internal class UnsupportedDRMTypeException(type: DRMType) :
|
|
|
23
23
|
|
|
24
24
|
internal class PlaybackException(reason: String?, cause: Throwable? = null) :
|
|
25
25
|
CodedException("A playback exception has occurred: ${reason ?: "reason unknown"}", cause)
|
|
26
|
+
|
|
27
|
+
internal class FailedToGetAudioFocusManagerException :
|
|
28
|
+
CodedException("Failed to get AudioFocusManager service")
|
|
@@ -139,9 +139,7 @@ class VideoModule : Module() {
|
|
|
139
139
|
|
|
140
140
|
Class(VideoPlayer::class) {
|
|
141
141
|
Constructor { source: VideoSource ->
|
|
142
|
-
|
|
143
|
-
VideoManager.registerVideoSourceToMediaItem(mediaItem, source)
|
|
144
|
-
VideoPlayer(activity.applicationContext, appContext, mediaItem)
|
|
142
|
+
VideoPlayer(activity.applicationContext, appContext, source)
|
|
145
143
|
}
|
|
146
144
|
|
|
147
145
|
Property("playing")
|
|
@@ -206,6 +204,16 @@ class VideoModule : Module() {
|
|
|
206
204
|
}
|
|
207
205
|
}
|
|
208
206
|
|
|
207
|
+
Property("showNowPlayingNotification")
|
|
208
|
+
.get { ref: VideoPlayer ->
|
|
209
|
+
ref.showNowPlayingNotification
|
|
210
|
+
}
|
|
211
|
+
.set { ref: VideoPlayer, showNotification: Boolean ->
|
|
212
|
+
appContext.mainQueue.launch {
|
|
213
|
+
ref.showNowPlayingNotification = showNotification
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
209
217
|
Property("status")
|
|
210
218
|
.get { ref: VideoPlayer ->
|
|
211
219
|
ref.status
|
|
@@ -255,6 +263,7 @@ class VideoModule : Module() {
|
|
|
255
263
|
VideoManager.registerVideoSourceToMediaItem(mediaItem, videoSource)
|
|
256
264
|
|
|
257
265
|
appContext.mainQueue.launch {
|
|
266
|
+
ref.videoSource = videoSource
|
|
258
267
|
ref.player.setMediaItem(mediaItem)
|
|
259
268
|
}
|
|
260
269
|
}
|
|
@@ -24,16 +24,18 @@ import expo.modules.kotlin.sharedobjects.SharedObject
|
|
|
24
24
|
import expo.modules.video.enums.PlayerStatus
|
|
25
25
|
import expo.modules.video.enums.PlayerStatus.*
|
|
26
26
|
import expo.modules.video.records.PlaybackError
|
|
27
|
+
import expo.modules.video.records.VideoSource
|
|
27
28
|
import expo.modules.video.records.VolumeEvent
|
|
28
29
|
import kotlinx.coroutines.launch
|
|
30
|
+
import java.lang.ref.WeakReference
|
|
29
31
|
|
|
30
32
|
// https://developer.android.com/guide/topics/media/media3/getting-started/migration-guide#improvements_in_media3
|
|
31
33
|
@UnstableApi
|
|
32
|
-
class VideoPlayer(context: Context, appContext: AppContext,
|
|
34
|
+
class VideoPlayer(context: Context, appContext: AppContext, source: VideoSource?) : AutoCloseable, SharedObject(appContext) {
|
|
33
35
|
// This improves the performance of playing DRM-protected content
|
|
34
36
|
private var renderersFactory = DefaultRenderersFactory(context)
|
|
35
37
|
.forceEnableMediaCodecAsynchronousQueueing()
|
|
36
|
-
|
|
38
|
+
val audioFocusManager = VideoPlayerAudioFocusManager(context, WeakReference(this))
|
|
37
39
|
val player = ExoPlayer
|
|
38
40
|
.Builder(context, renderersFactory)
|
|
39
41
|
.setLooper(context.mainLooper)
|
|
@@ -48,21 +50,19 @@ class VideoPlayer(context: Context, appContext: AppContext, private val mediaIte
|
|
|
48
50
|
field = value
|
|
49
51
|
}
|
|
50
52
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
var
|
|
54
|
-
set(
|
|
55
|
-
if (field !=
|
|
56
|
-
|
|
57
|
-
val newVideoSource = VideoManager.getVideoSourceFromMediaItem(newMediaItem)
|
|
58
|
-
|
|
59
|
-
sendEventOnJSThread("sourceChange", newVideoSource, oldVideoSource)
|
|
53
|
+
// This is used only for sending events and keeping the reference to the video source for the
|
|
54
|
+
// VideoManager, which holds weak references. Changing this will not affect the player.
|
|
55
|
+
var videoSource: VideoSource? = source
|
|
56
|
+
set(videoSource) {
|
|
57
|
+
if (field != videoSource) {
|
|
58
|
+
sendEventOnJSThread("sourceChange", videoSource, field)
|
|
60
59
|
}
|
|
61
|
-
field =
|
|
60
|
+
field = videoSource
|
|
62
61
|
}
|
|
63
62
|
|
|
64
63
|
// Volume of the player if there was no mute applied.
|
|
65
64
|
var userVolume = 1f
|
|
65
|
+
var status: PlayerStatus = IDLE
|
|
66
66
|
var requiresLinearPlayback = false
|
|
67
67
|
var staysActiveInBackground = false
|
|
68
68
|
var preservesPitch = false
|
|
@@ -70,6 +70,11 @@ class VideoPlayer(context: Context, appContext: AppContext, private val mediaIte
|
|
|
70
70
|
playbackParameters = applyPitchCorrection(playbackParameters)
|
|
71
71
|
field = preservesPitch
|
|
72
72
|
}
|
|
73
|
+
var showNowPlayingNotification = true
|
|
74
|
+
set(value) {
|
|
75
|
+
field = value
|
|
76
|
+
playbackServiceBinder?.service?.setShowNotification(value, this.player)
|
|
77
|
+
}
|
|
73
78
|
|
|
74
79
|
private var serviceConnection: ServiceConnection
|
|
75
80
|
internal var playbackServiceBinder: PlaybackServiceBinder? = null
|
|
@@ -89,6 +94,7 @@ class VideoPlayer(context: Context, appContext: AppContext, private val mediaIte
|
|
|
89
94
|
sendEventOnJSThread("volumeChange", VolumeEvent(volume, muted), VolumeEvent(volume, field))
|
|
90
95
|
player.volume = if (muted) 0f else userVolume
|
|
91
96
|
field = muted
|
|
97
|
+
audioFocusManager.onPlayerChangedAudioFocusProperty(this@VideoPlayer)
|
|
92
98
|
}
|
|
93
99
|
|
|
94
100
|
var playbackParameters: PlaybackParameters = PlaybackParameters.DEFAULT
|
|
@@ -107,6 +113,7 @@ class VideoPlayer(context: Context, appContext: AppContext, private val mediaIte
|
|
|
107
113
|
private val playerListener = object : Player.Listener {
|
|
108
114
|
override fun onIsPlayingChanged(isPlaying: Boolean) {
|
|
109
115
|
this@VideoPlayer.playing = isPlaying
|
|
116
|
+
audioFocusManager.onPlayerChangedAudioFocusProperty(this@VideoPlayer)
|
|
110
117
|
}
|
|
111
118
|
|
|
112
119
|
override fun onTimelineChanged(timeline: Timeline, reason: Int) {
|
|
@@ -114,7 +121,8 @@ class VideoPlayer(context: Context, appContext: AppContext, private val mediaIte
|
|
|
114
121
|
}
|
|
115
122
|
|
|
116
123
|
override fun onMediaItemTransition(mediaItem: MediaItem?, reason: Int) {
|
|
117
|
-
|
|
124
|
+
val newVideoSource = VideoManager.getVideoSourceFromMediaItem(mediaItem)
|
|
125
|
+
this@VideoPlayer.videoSource = newVideoSource
|
|
118
126
|
if (reason == Player.MEDIA_ITEM_TRANSITION_REASON_REPEAT) {
|
|
119
127
|
sendEventOnJSThread("playToEnd")
|
|
120
128
|
}
|
|
@@ -131,6 +139,7 @@ class VideoPlayer(context: Context, appContext: AppContext, private val mediaIte
|
|
|
131
139
|
|
|
132
140
|
override fun onVolumeChanged(volume: Float) {
|
|
133
141
|
this@VideoPlayer.volume = volume
|
|
142
|
+
audioFocusManager.onPlayerChangedAudioFocusProperty(this@VideoPlayer)
|
|
134
143
|
}
|
|
135
144
|
|
|
136
145
|
override fun onPlaybackParametersChanged(playbackParameters: PlaybackParameters) {
|
|
@@ -194,6 +203,7 @@ class VideoPlayer(context: Context, appContext: AppContext, private val mediaIte
|
|
|
194
203
|
}
|
|
195
204
|
|
|
196
205
|
override fun close() {
|
|
206
|
+
audioFocusManager.onPlayerDestroyed()
|
|
197
207
|
appContext?.reactContext?.unbindService(serviceConnection)
|
|
198
208
|
playbackServiceBinder?.service?.unregisterPlayer(player)
|
|
199
209
|
VideoManager.unregisterVideoPlayer(this@VideoPlayer)
|
|
@@ -202,6 +212,7 @@ class VideoPlayer(context: Context, appContext: AppContext, private val mediaIte
|
|
|
202
212
|
player.removeListener(playerListener)
|
|
203
213
|
player.release()
|
|
204
214
|
}
|
|
215
|
+
videoSource = null
|
|
205
216
|
}
|
|
206
217
|
|
|
207
218
|
override fun deallocate() {
|
|
@@ -216,8 +227,15 @@ class VideoPlayer(context: Context, appContext: AppContext, private val mediaIte
|
|
|
216
227
|
}
|
|
217
228
|
|
|
218
229
|
fun prepare() {
|
|
219
|
-
|
|
220
|
-
|
|
230
|
+
videoSource?.let { videoSource ->
|
|
231
|
+
val mediaItem = videoSource.toMediaItem()
|
|
232
|
+
VideoManager.registerVideoSourceToMediaItem(mediaItem, videoSource)
|
|
233
|
+
player.setMediaItem(mediaItem)
|
|
234
|
+
player.prepare()
|
|
235
|
+
} ?: run {
|
|
236
|
+
player.removeMediaItem(0)
|
|
237
|
+
player.prepare()
|
|
238
|
+
}
|
|
221
239
|
}
|
|
222
240
|
|
|
223
241
|
private fun applyPitchCorrection(playbackParameters: PlaybackParameters): PlaybackParameters {
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
package expo.modules.video
|
|
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 java.lang.ref.WeakReference
|
|
10
|
+
|
|
11
|
+
@UnstableApi
|
|
12
|
+
class VideoPlayerAudioFocusManager(val context: Context, private val player: WeakReference<VideoPlayer>) : AudioManager.OnAudioFocusChangeListener {
|
|
13
|
+
private val audioManager by lazy {
|
|
14
|
+
context.getSystemService(Context.AUDIO_SERVICE) as? AudioManager ?: run {
|
|
15
|
+
throw FailedToGetAudioFocusManagerException()
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
private var currentFocusRequest: AudioFocusRequest? = null
|
|
19
|
+
private var volumeBeforeDuck: Float = -1f
|
|
20
|
+
|
|
21
|
+
// TODO: @behenate (SDK52) Instead of calling this explicitly in the player we should add functionality
|
|
22
|
+
// To register as a player delegate and react to player events.
|
|
23
|
+
fun onPlayerChangedAudioFocusProperty(player: VideoPlayer) {
|
|
24
|
+
if (player.playing && !player.muted && player.volume > 0) {
|
|
25
|
+
requestAudioFocus()
|
|
26
|
+
} else {
|
|
27
|
+
abandonAudioFocus()
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
fun onPlayerDestroyed() {
|
|
32
|
+
abandonAudioFocus()
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
private fun requestAudioFocus() {
|
|
36
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
37
|
+
if (currentFocusRequest != null) {
|
|
38
|
+
abandonAudioFocus()
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
val newFocusRequest = AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN).run {
|
|
42
|
+
setAudioAttributes(
|
|
43
|
+
AudioAttributes.Builder().run {
|
|
44
|
+
setUsage(AudioAttributes.USAGE_MEDIA)
|
|
45
|
+
setContentType(AudioAttributes.CONTENT_TYPE_MOVIE)
|
|
46
|
+
setOnAudioFocusChangeListener(this@VideoPlayerAudioFocusManager)
|
|
47
|
+
build()
|
|
48
|
+
}
|
|
49
|
+
).build()
|
|
50
|
+
}
|
|
51
|
+
this.currentFocusRequest = newFocusRequest
|
|
52
|
+
audioManager.requestAudioFocus(newFocusRequest)
|
|
53
|
+
} else {
|
|
54
|
+
audioManager.requestAudioFocus(
|
|
55
|
+
this,
|
|
56
|
+
AudioManager.STREAM_MUSIC,
|
|
57
|
+
AudioManager.AUDIOFOCUS_GAIN
|
|
58
|
+
)
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
private fun abandonAudioFocus() {
|
|
63
|
+
currentFocusRequest?.let {
|
|
64
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
65
|
+
audioManager.abandonAudioFocusRequest(it)
|
|
66
|
+
} else {
|
|
67
|
+
audioManager.abandonAudioFocus(this)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
currentFocusRequest = null
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
override fun onAudioFocusChange(focusChange: Int) {
|
|
74
|
+
when (focusChange) {
|
|
75
|
+
AudioManager.AUDIOFOCUS_LOSS -> {
|
|
76
|
+
player.get()?.player?.pause()
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
AudioManager.AUDIOFOCUS_LOSS_TRANSIENT -> {
|
|
80
|
+
player.get()?.player?.pause()
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK -> {
|
|
84
|
+
player.get()?.let { player ->
|
|
85
|
+
volumeBeforeDuck = player.player.volume
|
|
86
|
+
player.volume = player.volume / 2f
|
|
87
|
+
player.userVolume = player.volume
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
AudioManager.AUDIOFOCUS_GAIN -> {
|
|
92
|
+
// TODO: For now this behaves like iOS and doesn't resume playback automatically
|
|
93
|
+
// In future versions we can add a prop to control this behavior.
|
|
94
|
+
player.get()?.let { player ->
|
|
95
|
+
if (player.playing && !player.muted && player.volume > 0) {
|
|
96
|
+
player.volume = volumeBeforeDuck
|
|
97
|
+
player.userVolume = player.volume
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
package expo.modules.video.records
|
|
2
|
+
|
|
3
|
+
import expo.modules.kotlin.records.Field
|
|
4
|
+
import expo.modules.kotlin.records.Record
|
|
5
|
+
import java.io.Serializable
|
|
6
|
+
|
|
7
|
+
class VideoMetadata(
|
|
8
|
+
@Field var title: String? = null,
|
|
9
|
+
@Field var artist: String? = null
|
|
10
|
+
) : Record, Serializable
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
package expo.modules.video.records
|
|
2
2
|
|
|
3
3
|
import androidx.media3.common.MediaItem
|
|
4
|
+
import androidx.media3.common.MediaMetadata
|
|
4
5
|
import expo.modules.kotlin.records.Field
|
|
5
6
|
import expo.modules.kotlin.records.Record
|
|
6
7
|
import expo.modules.video.UnsupportedDRMTypeException
|
|
@@ -8,7 +9,8 @@ import java.io.Serializable
|
|
|
8
9
|
|
|
9
10
|
class VideoSource(
|
|
10
11
|
@Field var uri: String? = null,
|
|
11
|
-
@Field var drm: DRMOptions? = null
|
|
12
|
+
@Field var drm: DRMOptions? = null,
|
|
13
|
+
@Field var metadata: VideoMetadata? = null
|
|
12
14
|
) : Record, Serializable {
|
|
13
15
|
private fun toMediaId(): String {
|
|
14
16
|
return "uri:${this.uri}" +
|
|
@@ -16,13 +18,16 @@ class VideoSource(
|
|
|
16
18
|
"DrmLicenseServer:${this.drm?.licenseServer}" +
|
|
17
19
|
"DrmMultiKey:${this.drm?.multiKey}" +
|
|
18
20
|
"DRMHeadersKeys:${this.drm?.headers?.keys?.joinToString {it}}}" +
|
|
19
|
-
"DRMHeadersValues:${this.drm?.headers?.values?.joinToString {it}}}"
|
|
21
|
+
"DRMHeadersValues:${this.drm?.headers?.values?.joinToString {it}}}" +
|
|
22
|
+
"NotificationDataTitle:${this.metadata?.title}" +
|
|
23
|
+
"NotificationDataSecondaryText:${this.metadata?.artist}"
|
|
20
24
|
}
|
|
21
25
|
|
|
22
26
|
fun toMediaItem() = MediaItem
|
|
23
27
|
.Builder()
|
|
24
28
|
.apply {
|
|
25
29
|
setUri(uri ?: "")
|
|
30
|
+
setMediaId(toMediaId())
|
|
26
31
|
drm?.let {
|
|
27
32
|
if (it.type.isSupported()) {
|
|
28
33
|
setDrmConfiguration(it.toDRMConfiguration())
|
|
@@ -30,6 +35,14 @@ class VideoSource(
|
|
|
30
35
|
throw UnsupportedDRMTypeException(it.type)
|
|
31
36
|
}
|
|
32
37
|
}
|
|
38
|
+
setMediaMetadata(
|
|
39
|
+
MediaMetadata.Builder().apply {
|
|
40
|
+
metadata?.let { data ->
|
|
41
|
+
setTitle(data.title)
|
|
42
|
+
setArtist(data.artist)
|
|
43
|
+
}
|
|
44
|
+
}.build()
|
|
45
|
+
)
|
|
33
46
|
}
|
|
34
47
|
.build()
|
|
35
48
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import type { VideoPlayer } from './VideoView.types';
|
|
2
|
+
type ExpoVideoModule = {
|
|
3
|
+
VideoPlayer: typeof VideoPlayer;
|
|
4
|
+
isPictureInPictureSupported(): boolean;
|
|
5
|
+
};
|
|
6
|
+
declare const _default: ExpoVideoModule;
|
|
5
7
|
export default _default;
|
|
6
8
|
//# sourceMappingURL=NativeVideoModule.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NativeVideoModule.d.ts","sourceRoot":"","sources":["../src/NativeVideoModule.ts"],"names":[],"mappings":"AAEA;;
|
|
1
|
+
{"version":3,"file":"NativeVideoModule.d.ts","sourceRoot":"","sources":["../src/NativeVideoModule.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,KAAK,eAAe,GAAG;IACrB,WAAW,EAAE,OAAO,WAAW,CAAC;IAChC,2BAA2B,IAAI,OAAO,CAAC;CACxC,CAAC;;AAEF,wBAAiE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NativeVideoModule.js","sourceRoot":"","sources":["../src/NativeVideoModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"NativeVideoModule.js","sourceRoot":"","sources":["../src/NativeVideoModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AASxD,eAAe,mBAAmB,CAAkB,WAAW,CAAC,CAAC","sourcesContent":["import { requireNativeModule } from 'expo-modules-core';\n\nimport type { VideoPlayer } from './VideoView.types';\n\ntype ExpoVideoModule = {\n VideoPlayer: typeof VideoPlayer;\n isPictureInPictureSupported(): boolean;\n};\n\nexport default requireNativeModule<ExpoVideoModule>('ExpoVideo');\n"]}
|
package/build/VideoPlayer.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { VideoPlayer, VideoSource } from './VideoView.types';
|
|
1
|
+
import type { VideoPlayer, VideoSource } from './VideoView.types';
|
|
2
2
|
/**
|
|
3
3
|
* Creates a `VideoPlayer`, which will be automatically cleaned up when the component is unmounted.
|
|
4
4
|
* @param source - A video source that is used to initialize the player.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VideoPlayer.d.ts","sourceRoot":"","sources":["../src/VideoPlayer.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"VideoPlayer.d.ts","sourceRoot":"","sources":["../src/VideoPlayer.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAElE;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,WAAW,EACnB,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,IAAI,GACpC,WAAW,CAQb"}
|
package/build/VideoPlayer.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VideoPlayer.js","sourceRoot":"","sources":["../src/VideoPlayer.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAE7D,OAAO,iBAAiB,MAAM,qBAAqB,CAAC;AAGpD;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAC5B,MAAmB,EACnB,KAAqC;IAErC,MAAM,YAAY,GAAG,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC;IAE3E,OAAO,wBAAwB,CAAC,GAAG,EAAE;QACnC,MAAM,MAAM,GAAG,IAAI,iBAAiB,CAAC,WAAW,CAAC,YAAY,CAAC,CAAC;QAC/D,KAAK,EAAE,CAAC,MAAM,CAAC,CAAC;QAChB,OAAO,MAAM,CAAC;IAChB,CAAC,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;AACrC,CAAC","sourcesContent":["import { useReleasingSharedObject } from 'expo-modules-core';\n\nimport NativeVideoModule from './NativeVideoModule';\nimport { VideoPlayer, VideoSource } from './VideoView.types';\n\n/**\n * Creates a `VideoPlayer`, which will be automatically cleaned up when the component is unmounted.\n * @param source - A video source that is used to initialize the player.\n * @param setup - A function that allows setting up the player. It will run after the player is created.\n */\nexport function useVideoPlayer(\n source: VideoSource,\n setup?: (player: VideoPlayer) => void\n): VideoPlayer {\n const parsedSource = typeof source === 'string' ? { uri: source } : source;\n\n return useReleasingSharedObject(() => {\n const player = new NativeVideoModule.VideoPlayer(parsedSource);\n setup?.(player);\n return player;\n }, [JSON.stringify(parsedSource)]);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"VideoPlayer.js","sourceRoot":"","sources":["../src/VideoPlayer.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAE7D,OAAO,iBAAiB,MAAM,qBAAqB,CAAC;AAGpD;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAC5B,MAAmB,EACnB,KAAqC;IAErC,MAAM,YAAY,GAAG,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC;IAE3E,OAAO,wBAAwB,CAAC,GAAG,EAAE;QACnC,MAAM,MAAM,GAAG,IAAI,iBAAiB,CAAC,WAAW,CAAC,YAAY,CAAC,CAAC;QAC/D,KAAK,EAAE,CAAC,MAAM,CAAC,CAAC;QAChB,OAAO,MAAM,CAAC;IAChB,CAAC,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;AACrC,CAAC","sourcesContent":["import { useReleasingSharedObject } from 'expo-modules-core';\n\nimport NativeVideoModule from './NativeVideoModule';\nimport type { VideoPlayer, VideoSource } from './VideoView.types';\n\n/**\n * Creates a `VideoPlayer`, which will be automatically cleaned up when the component is unmounted.\n * @param source - A video source that is used to initialize the player.\n * @param setup - A function that allows setting up the player. It will run after the player is created.\n */\nexport function useVideoPlayer(\n source: VideoSource,\n setup?: (player: VideoPlayer) => void\n): VideoPlayer {\n const parsedSource = typeof source === 'string' ? { uri: source } : source;\n\n return useReleasingSharedObject(() => {\n const player = new NativeVideoModule.VideoPlayer(parsedSource);\n setup?.(player);\n return player;\n }, [JSON.stringify(parsedSource)]);\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { VideoPlayer, VideoPlayerEvents, VideoPlayerStatus, VideoSource } from './VideoView.types';
|
|
1
|
+
import type { VideoPlayer, VideoPlayerEvents, VideoPlayerStatus, VideoSource } from './VideoView.types';
|
|
2
2
|
export declare function useVideoPlayer(source: VideoSource, setup?: (player: VideoPlayer) => void): VideoPlayer;
|
|
3
3
|
export declare function getSourceUri(source: VideoSource): string | null;
|
|
4
4
|
export declare class VideoPlayerWeb extends globalThis.expo.SharedObject<VideoPlayerEvents> implements VideoPlayer {
|
|
@@ -14,6 +14,7 @@ export declare class VideoPlayerWeb extends globalThis.expo.SharedObject<VideoPl
|
|
|
14
14
|
_preservesPitch: boolean;
|
|
15
15
|
_status: VideoPlayerStatus;
|
|
16
16
|
staysActiveInBackground: boolean;
|
|
17
|
+
showNowPlayingNotification: boolean;
|
|
17
18
|
set muted(value: boolean);
|
|
18
19
|
get muted(): boolean;
|
|
19
20
|
set playbackRate(value: number);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VideoPlayer.web.d.ts","sourceRoot":"","sources":["../src/VideoPlayer.web.tsx"],"names":[],"mappings":"AAEA,OAAO,
|
|
1
|
+
{"version":3,"file":"VideoPlayer.web.d.ts","sourceRoot":"","sources":["../src/VideoPlayer.web.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,WAAW,EACX,iBAAiB,EACjB,iBAAiB,EACjB,WAAW,EACZ,MAAM,mBAAmB,CAAC;AAE3B,wBAAgB,cAAc,CAC5B,MAAM,EAAE,WAAW,EACnB,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,IAAI,GACpC,WAAW,CAQb;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,GAAG,IAAI,CAK/D;AAED,qBAAa,cACX,SAAQ,UAAU,CAAC,IAAI,CAAC,YAAY,CAAC,iBAAiB,CACtD,YAAW,WAAW;gBAEV,MAAM,EAAE,WAAW;IAK/B,GAAG,EAAE,WAAW,CAAQ;IACxB,cAAc,EAAE,GAAG,CAAC,gBAAgB,CAAC,CAAa;IAClD,WAAW,EAAE,GAAG,CAAC,2BAA2B,CAAC,CAAa;IAC1D,OAAO,EAAE,OAAO,CAAS;IACzB,MAAM,EAAE,OAAO,CAAS;IACxB,OAAO,EAAE,MAAM,CAAK;IACpB,KAAK,EAAE,OAAO,CAAS;IACvB,aAAa,EAAE,MAAM,CAAO;IAC5B,eAAe,EAAE,OAAO,CAAQ;IAChC,OAAO,EAAE,iBAAiB,CAAU;IACpC,uBAAuB,EAAE,OAAO,CAAS;IACzC,0BAA0B,EAAE,OAAO,CAAS;IAE5C,IAAI,KAAK,CAAC,KAAK,EAAE,OAAO,EAKvB;IAED,IAAI,KAAK,IAAI,OAAO,CAEnB;IAED,IAAI,YAAY,CAAC,KAAK,EAAE,MAAM,EAI7B;IAED,IAAI,YAAY,IAAI,MAAM,CAEzB;IAED,IAAI,MAAM,CAAC,KAAK,EAAE,MAAM,EAKvB;IAED,IAAI,MAAM,IAAI,MAAM,CAKnB;IAED,IAAI,IAAI,CAAC,KAAK,EAAE,OAAO,EAKtB;IAED,IAAI,IAAI,IAAI,OAAO,CAElB;IAED,IAAI,WAAW,IAAI,MAAM,CAGxB;IAED,IAAI,WAAW,CAAC,KAAK,EAAE,MAAM,EAI5B;IAED,IAAI,cAAc,IAAI,OAAO,CAE5B;IAED,IAAI,cAAc,CAAC,KAAK,EAAE,OAAO,EAKhC;IAED,IAAI,MAAM,IAAI,iBAAiB,CAE9B;IAED,cAAc,CAAC,KAAK,EAAE,gBAAgB;IAMtC,gBAAgB,CAAC,KAAK,EAAE,gBAAgB;IAIxC,cAAc,CACZ,YAAY,EAAE,YAAY,EAC1B,YAAY,EAAE,QAAQ,EACtB,eAAe,EAAE,2BAA2B,GAC3C,IAAI;IAYP,gBAAgB,CACd,KAAK,EAAE,gBAAgB,EACvB,YAAY,EAAE,YAAY,EAC1B,eAAe,EAAE,2BAA2B;IAe9C,IAAI,IAAI,IAAI;IAOZ,KAAK,IAAI,IAAI;IAOb,OAAO,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI;IAelC,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAM7B,MAAM,IAAI,IAAI;IAQd,0BAA0B,CAAC,KAAK,EAAE,gBAAgB,GAAG,IAAI;IAezD,aAAa,CAAC,KAAK,EAAE,gBAAgB,GAAG,IAAI;CA0D7C"}
|
package/build/VideoPlayer.web.js
CHANGED
|
@@ -29,6 +29,7 @@ export class VideoPlayerWeb extends globalThis.expo.SharedObject {
|
|
|
29
29
|
_preservesPitch = true;
|
|
30
30
|
_status = 'idle';
|
|
31
31
|
staysActiveInBackground = false; // Not supported on web. Dummy to match the interface.
|
|
32
|
+
showNowPlayingNotification = false; // Not supported on web. Dummy to match the interface.
|
|
32
33
|
set muted(value) {
|
|
33
34
|
this._mountedVideos.forEach((video) => {
|
|
34
35
|
video.muted = value;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VideoPlayer.web.js","sourceRoot":"","sources":["../src/VideoPlayer.web.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useMemo } from 'react';\n\nimport { VideoPlayer, VideoPlayerEvents, VideoPlayerStatus, VideoSource } from './VideoView.types';\n\nexport function useVideoPlayer(\n source: VideoSource,\n setup?: (player: VideoPlayer) => void\n): VideoPlayer {\n const parsedSource = typeof source === 'string' ? { uri: source } : source;\n\n return useMemo(() => {\n const player = new VideoPlayerWeb(parsedSource);\n setup?.(player);\n return player;\n }, [JSON.stringify(source)]);\n}\n\nexport function getSourceUri(source: VideoSource): string | null {\n if (typeof source == 'string') {\n return source;\n }\n return source?.uri ?? null;\n}\n\nexport class VideoPlayerWeb\n extends globalThis.expo.SharedObject<VideoPlayerEvents>\n implements VideoPlayer\n{\n constructor(source: VideoSource) {\n super();\n this.src = source;\n }\n\n src: VideoSource = null;\n _mountedVideos: Set<HTMLVideoElement> = new Set();\n _audioNodes: Set<MediaElementAudioSourceNode> = new Set();\n playing: boolean = false;\n _muted: boolean = false;\n _volume: number = 1;\n _loop: boolean = false;\n _playbackRate: number = 1.0;\n _preservesPitch: boolean = true;\n _status: VideoPlayerStatus = 'idle';\n staysActiveInBackground: boolean = false; // Not supported on web. Dummy to match the interface.\n\n set muted(value: boolean) {\n this._mountedVideos.forEach((video) => {\n video.muted = value;\n });\n this._muted = value;\n }\n\n get muted(): boolean {\n return this._muted;\n }\n\n set playbackRate(value: number) {\n this._mountedVideos.forEach((video) => {\n video.playbackRate = value;\n });\n }\n\n get playbackRate(): number {\n return this._playbackRate;\n }\n\n set volume(value: number) {\n this._mountedVideos.forEach((video) => {\n video.volume = value;\n });\n this._volume = value;\n }\n\n get volume(): number {\n this._mountedVideos.forEach((video) => {\n this._volume = video.volume;\n });\n return this._volume;\n }\n\n set loop(value: boolean) {\n this._mountedVideos.forEach((video) => {\n video.loop = value;\n });\n this._loop = value;\n }\n\n get loop(): boolean {\n return this._loop;\n }\n\n get currentTime(): number {\n // All videos should be synchronized, so we return the position of the first video.\n return [...this._mountedVideos][0].currentTime;\n }\n\n set currentTime(value: number) {\n this._mountedVideos.forEach((video) => {\n video.currentTime = value;\n });\n }\n\n get preservesPitch(): boolean {\n return this._preservesPitch;\n }\n\n set preservesPitch(value: boolean) {\n this._mountedVideos.forEach((video) => {\n video.preservesPitch = value;\n });\n this._preservesPitch = value;\n }\n\n get status(): VideoPlayerStatus {\n return this._status;\n }\n\n mountVideoView(video: HTMLVideoElement) {\n this._mountedVideos.add(video);\n this._addListeners(video);\n this._synchronizeWithFirstVideo(video);\n }\n\n unmountVideoView(video: HTMLVideoElement) {\n this._mountedVideos.delete(video);\n }\n\n mountAudioNode(\n audioContext: AudioContext,\n zeroGainNode: GainNode,\n audioSourceNode: MediaElementAudioSourceNode\n ): void {\n if (!audioContext || !zeroGainNode) return;\n\n this._audioNodes.add(audioSourceNode);\n // First mounted video should be connected to the audio context. All other videos have to be muted.\n if (this._audioNodes.size === 1) {\n audioSourceNode.connect(audioContext.destination);\n } else {\n audioSourceNode.connect(zeroGainNode);\n }\n }\n\n unmountAudioNode(\n video: HTMLVideoElement,\n audioContext: AudioContext,\n audioSourceNode: MediaElementAudioSourceNode\n ) {\n const mountedVideos = [...this._mountedVideos];\n const videoPlayingAudio = mountedVideos[0];\n this._audioNodes.delete(audioSourceNode);\n audioSourceNode.disconnect();\n\n // If video playing audio has been removed, select a new video to be the audio player by disconnecting it from the mute node.\n if (videoPlayingAudio === video && this._audioNodes.size > 0 && audioContext) {\n const newMainAudioSource = [...this._audioNodes][0];\n newMainAudioSource.disconnect();\n newMainAudioSource.connect(audioContext.destination);\n }\n }\n\n play(): void {\n this._mountedVideos.forEach((video) => {\n video.play();\n });\n this.playing = true;\n }\n\n pause(): void {\n this._mountedVideos.forEach((video) => {\n video.pause();\n });\n this.playing = false;\n }\n\n replace(source: VideoSource): void {\n this._mountedVideos.forEach((video) => {\n const uri = getSourceUri(source);\n video.pause();\n if (uri) {\n video.setAttribute('src', uri);\n video.load();\n video.play();\n } else {\n video.removeAttribute('src');\n }\n });\n this.playing = true;\n }\n\n seekBy(seconds: number): void {\n this._mountedVideos.forEach((video) => {\n video.currentTime += seconds;\n });\n }\n\n replay(): void {\n this._mountedVideos.forEach((video) => {\n video.currentTime = 0;\n video.play();\n });\n this.playing = true;\n }\n\n _synchronizeWithFirstVideo(video: HTMLVideoElement): void {\n const firstVideo = [...this._mountedVideos][0];\n if (!firstVideo) return;\n\n if (firstVideo.paused) {\n video.pause();\n } else {\n video.play();\n }\n video.currentTime = firstVideo.currentTime;\n video.volume = firstVideo.volume;\n video.muted = firstVideo.muted;\n video.playbackRate = firstVideo.playbackRate;\n }\n\n _addListeners(video: HTMLVideoElement): void {\n video.onplay = () => {\n this.playing = true;\n this._mountedVideos.forEach((mountedVideo) => {\n mountedVideo.play();\n });\n };\n\n video.onpause = () => {\n this.playing = false;\n this._mountedVideos.forEach((mountedVideo) => {\n mountedVideo.pause();\n });\n };\n\n video.onvolumechange = () => {\n this.volume = video.volume;\n this.muted = video.muted;\n };\n\n video.onseeking = () => {\n this._mountedVideos.forEach((mountedVideo) => {\n if (mountedVideo === video || mountedVideo.currentTime === video.currentTime) return;\n mountedVideo.currentTime = video.currentTime;\n });\n };\n\n video.onseeked = () => {\n this._mountedVideos.forEach((mountedVideo) => {\n if (mountedVideo === video || mountedVideo.currentTime === video.currentTime) return;\n mountedVideo.currentTime = video.currentTime;\n });\n };\n\n video.onratechange = () => {\n this._mountedVideos.forEach((mountedVideo) => {\n if (mountedVideo === video || mountedVideo.playbackRate === video.playbackRate) return;\n this._playbackRate = video.playbackRate;\n mountedVideo.playbackRate = video.playbackRate;\n });\n };\n\n video.onerror = () => {\n this._status = 'error';\n };\n\n video.onloadeddata = () => {\n this._status = 'readyToPlay';\n\n if (this.playing && video.paused) {\n video.play();\n }\n };\n\n video.onwaiting = () => {\n this._status = 'loading';\n };\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"VideoPlayer.web.js","sourceRoot":"","sources":["../src/VideoPlayer.web.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useMemo } from 'react';\n\nimport type {\n VideoPlayer,\n VideoPlayerEvents,\n VideoPlayerStatus,\n VideoSource,\n} from './VideoView.types';\n\nexport function useVideoPlayer(\n source: VideoSource,\n setup?: (player: VideoPlayer) => void\n): VideoPlayer {\n const parsedSource = typeof source === 'string' ? { uri: source } : source;\n\n return useMemo(() => {\n const player = new VideoPlayerWeb(parsedSource);\n setup?.(player);\n return player;\n }, [JSON.stringify(source)]);\n}\n\nexport function getSourceUri(source: VideoSource): string | null {\n if (typeof source == 'string') {\n return source;\n }\n return source?.uri ?? null;\n}\n\nexport class VideoPlayerWeb\n extends globalThis.expo.SharedObject<VideoPlayerEvents>\n implements VideoPlayer\n{\n constructor(source: VideoSource) {\n super();\n this.src = source;\n }\n\n src: VideoSource = null;\n _mountedVideos: Set<HTMLVideoElement> = new Set();\n _audioNodes: Set<MediaElementAudioSourceNode> = new Set();\n playing: boolean = false;\n _muted: boolean = false;\n _volume: number = 1;\n _loop: boolean = false;\n _playbackRate: number = 1.0;\n _preservesPitch: boolean = true;\n _status: VideoPlayerStatus = 'idle';\n staysActiveInBackground: boolean = false; // Not supported on web. Dummy to match the interface.\n showNowPlayingNotification: boolean = false; // Not supported on web. Dummy to match the interface.\n\n set muted(value: boolean) {\n this._mountedVideos.forEach((video) => {\n video.muted = value;\n });\n this._muted = value;\n }\n\n get muted(): boolean {\n return this._muted;\n }\n\n set playbackRate(value: number) {\n this._mountedVideos.forEach((video) => {\n video.playbackRate = value;\n });\n }\n\n get playbackRate(): number {\n return this._playbackRate;\n }\n\n set volume(value: number) {\n this._mountedVideos.forEach((video) => {\n video.volume = value;\n });\n this._volume = value;\n }\n\n get volume(): number {\n this._mountedVideos.forEach((video) => {\n this._volume = video.volume;\n });\n return this._volume;\n }\n\n set loop(value: boolean) {\n this._mountedVideos.forEach((video) => {\n video.loop = value;\n });\n this._loop = value;\n }\n\n get loop(): boolean {\n return this._loop;\n }\n\n get currentTime(): number {\n // All videos should be synchronized, so we return the position of the first video.\n return [...this._mountedVideos][0].currentTime;\n }\n\n set currentTime(value: number) {\n this._mountedVideos.forEach((video) => {\n video.currentTime = value;\n });\n }\n\n get preservesPitch(): boolean {\n return this._preservesPitch;\n }\n\n set preservesPitch(value: boolean) {\n this._mountedVideos.forEach((video) => {\n video.preservesPitch = value;\n });\n this._preservesPitch = value;\n }\n\n get status(): VideoPlayerStatus {\n return this._status;\n }\n\n mountVideoView(video: HTMLVideoElement) {\n this._mountedVideos.add(video);\n this._addListeners(video);\n this._synchronizeWithFirstVideo(video);\n }\n\n unmountVideoView(video: HTMLVideoElement) {\n this._mountedVideos.delete(video);\n }\n\n mountAudioNode(\n audioContext: AudioContext,\n zeroGainNode: GainNode,\n audioSourceNode: MediaElementAudioSourceNode\n ): void {\n if (!audioContext || !zeroGainNode) return;\n\n this._audioNodes.add(audioSourceNode);\n // First mounted video should be connected to the audio context. All other videos have to be muted.\n if (this._audioNodes.size === 1) {\n audioSourceNode.connect(audioContext.destination);\n } else {\n audioSourceNode.connect(zeroGainNode);\n }\n }\n\n unmountAudioNode(\n video: HTMLVideoElement,\n audioContext: AudioContext,\n audioSourceNode: MediaElementAudioSourceNode\n ) {\n const mountedVideos = [...this._mountedVideos];\n const videoPlayingAudio = mountedVideos[0];\n this._audioNodes.delete(audioSourceNode);\n audioSourceNode.disconnect();\n\n // If video playing audio has been removed, select a new video to be the audio player by disconnecting it from the mute node.\n if (videoPlayingAudio === video && this._audioNodes.size > 0 && audioContext) {\n const newMainAudioSource = [...this._audioNodes][0];\n newMainAudioSource.disconnect();\n newMainAudioSource.connect(audioContext.destination);\n }\n }\n\n play(): void {\n this._mountedVideos.forEach((video) => {\n video.play();\n });\n this.playing = true;\n }\n\n pause(): void {\n this._mountedVideos.forEach((video) => {\n video.pause();\n });\n this.playing = false;\n }\n\n replace(source: VideoSource): void {\n this._mountedVideos.forEach((video) => {\n const uri = getSourceUri(source);\n video.pause();\n if (uri) {\n video.setAttribute('src', uri);\n video.load();\n video.play();\n } else {\n video.removeAttribute('src');\n }\n });\n this.playing = true;\n }\n\n seekBy(seconds: number): void {\n this._mountedVideos.forEach((video) => {\n video.currentTime += seconds;\n });\n }\n\n replay(): void {\n this._mountedVideos.forEach((video) => {\n video.currentTime = 0;\n video.play();\n });\n this.playing = true;\n }\n\n _synchronizeWithFirstVideo(video: HTMLVideoElement): void {\n const firstVideo = [...this._mountedVideos][0];\n if (!firstVideo) return;\n\n if (firstVideo.paused) {\n video.pause();\n } else {\n video.play();\n }\n video.currentTime = firstVideo.currentTime;\n video.volume = firstVideo.volume;\n video.muted = firstVideo.muted;\n video.playbackRate = firstVideo.playbackRate;\n }\n\n _addListeners(video: HTMLVideoElement): void {\n video.onplay = () => {\n this.playing = true;\n this._mountedVideos.forEach((mountedVideo) => {\n mountedVideo.play();\n });\n };\n\n video.onpause = () => {\n this.playing = false;\n this._mountedVideos.forEach((mountedVideo) => {\n mountedVideo.pause();\n });\n };\n\n video.onvolumechange = () => {\n this.volume = video.volume;\n this.muted = video.muted;\n };\n\n video.onseeking = () => {\n this._mountedVideos.forEach((mountedVideo) => {\n if (mountedVideo === video || mountedVideo.currentTime === video.currentTime) return;\n mountedVideo.currentTime = video.currentTime;\n });\n };\n\n video.onseeked = () => {\n this._mountedVideos.forEach((mountedVideo) => {\n if (mountedVideo === video || mountedVideo.currentTime === video.currentTime) return;\n mountedVideo.currentTime = video.currentTime;\n });\n };\n\n video.onratechange = () => {\n this._mountedVideos.forEach((mountedVideo) => {\n if (mountedVideo === video || mountedVideo.playbackRate === video.playbackRate) return;\n this._playbackRate = video.playbackRate;\n mountedVideo.playbackRate = video.playbackRate;\n });\n };\n\n video.onerror = () => {\n this._status = 'error';\n };\n\n video.onloadeddata = () => {\n this._status = 'readyToPlay';\n\n if (this.playing && video.paused) {\n video.play();\n }\n };\n\n video.onwaiting = () => {\n this._status = 'loading';\n };\n }\n}\n"]}
|
package/build/VideoView.d.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { ReactNode, PureComponent } from 'react';
|
|
2
|
-
import { VideoViewProps } from './VideoView.types';
|
|
2
|
+
import type { VideoViewProps } from './VideoView.types';
|
|
3
3
|
/**
|
|
4
4
|
* Returns whether the current device supports Picture in Picture (PiP) mode.
|
|
5
5
|
* @returns A `boolean` which is `true` if the device supports PiP mode, and `false` otherwise.
|
|
6
6
|
* @platform android
|
|
7
7
|
* @platform ios
|
|
8
8
|
*/
|
|
9
|
-
export declare function isPictureInPictureSupported():
|
|
9
|
+
export declare function isPictureInPictureSupported(): boolean;
|
|
10
10
|
export declare class VideoView extends PureComponent<VideoViewProps> {
|
|
11
11
|
nativeRef: import("react").RefObject<any>;
|
|
12
12
|
/**
|
package/build/VideoView.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VideoView.d.ts","sourceRoot":"","sources":["../src/VideoView.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,aAAa,EAAa,MAAM,OAAO,CAAC;AAI5D,OAAO,EAAe,cAAc,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"VideoView.d.ts","sourceRoot":"","sources":["../src/VideoView.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,aAAa,EAAa,MAAM,OAAO,CAAC;AAI5D,OAAO,KAAK,EAAe,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAErE;;;;;GAKG;AACH,wBAAgB,2BAA2B,IAAI,OAAO,CAErD;AAED,qBAAa,SAAU,SAAQ,aAAa,CAAC,cAAc,CAAC;IAC1D,SAAS,iCAAoB;IAE7B;;OAEG;IACH,eAAe;IAIf;;OAEG;IACH,cAAc;IAId;;;;;OAKG;IACH,qBAAqB,IAAI,IAAI;IAI7B;;;;OAIG;IACH,oBAAoB,IAAI,IAAI;IAI5B,MAAM,IAAI,SAAS;CAMpB"}
|
package/build/VideoView.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VideoView.js","sourceRoot":"","sources":["../src/VideoView.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAa,aAAa,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAE5D,OAAO,iBAAiB,MAAM,qBAAqB,CAAC;AACpD,OAAO,eAAe,MAAM,mBAAmB,CAAC;AAGhD;;;;;GAKG;AACH,MAAM,UAAU,2BAA2B;IACzC,OAAO,iBAAiB,CAAC,2BAA2B,EAAE,CAAC;AACzD,CAAC;AAED,MAAM,OAAO,SAAU,SAAQ,aAA6B;IAC1D,SAAS,GAAG,SAAS,EAAO,CAAC;IAE7B;;OAEG;IACH,eAAe;QACb,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,eAAe,EAAE,CAAC;IAC5C,CAAC;IAED;;OAEG;IACH,cAAc;QACZ,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,cAAc,EAAE,CAAC;IAC3C,CAAC;IAED;;;;;OAKG;IACH,qBAAqB;QACnB,OAAO,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,qBAAqB,EAAE,CAAC;IACzD,CAAC;IAED;;;;OAIG;IACH,oBAAoB;QAClB,OAAO,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,oBAAoB,EAAE,CAAC;IACxD,CAAC;IAED,MAAM;QACJ,MAAM,EAAE,MAAM,EAAE,GAAG,KAAK,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;QACxC,MAAM,QAAQ,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;QAErC,OAAO,CAAC,eAAe,CAAC,IAAI,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,EAAG,CAAC;IAC/E,CAAC;CACF;AAED,gFAAgF;AAChF,gEAAgE;AAChE,yEAAyE;AACzE,SAAS,WAAW,CAAC,MAA4B;IAC/C,IAAI,MAAM,YAAY,iBAAiB,CAAC,WAAW,EAAE;QACnD,mBAAmB;QACnB,OAAO,MAAM,CAAC,yBAAyB,CAAC;KACzC;IACD,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE;QAC9B,OAAO,MAAM,CAAC;KACf;IACD,OAAO,IAAI,CAAC;AACd,CAAC","sourcesContent":["import { ReactNode, PureComponent, createRef } from 'react';\n\nimport NativeVideoModule from './NativeVideoModule';\nimport NativeVideoView from './NativeVideoView';\nimport { VideoPlayer, VideoViewProps } from './VideoView.types';\n\n/**\n * Returns whether the current device supports Picture in Picture (PiP) mode.\n * @returns A `boolean` which is `true` if the device supports PiP mode, and `false` otherwise.\n * @platform android\n * @platform ios\n */\nexport function isPictureInPictureSupported():
|
|
1
|
+
{"version":3,"file":"VideoView.js","sourceRoot":"","sources":["../src/VideoView.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAa,aAAa,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAE5D,OAAO,iBAAiB,MAAM,qBAAqB,CAAC;AACpD,OAAO,eAAe,MAAM,mBAAmB,CAAC;AAGhD;;;;;GAKG;AACH,MAAM,UAAU,2BAA2B;IACzC,OAAO,iBAAiB,CAAC,2BAA2B,EAAE,CAAC;AACzD,CAAC;AAED,MAAM,OAAO,SAAU,SAAQ,aAA6B;IAC1D,SAAS,GAAG,SAAS,EAAO,CAAC;IAE7B;;OAEG;IACH,eAAe;QACb,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,eAAe,EAAE,CAAC;IAC5C,CAAC;IAED;;OAEG;IACH,cAAc;QACZ,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,cAAc,EAAE,CAAC;IAC3C,CAAC;IAED;;;;;OAKG;IACH,qBAAqB;QACnB,OAAO,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,qBAAqB,EAAE,CAAC;IACzD,CAAC;IAED;;;;OAIG;IACH,oBAAoB;QAClB,OAAO,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,oBAAoB,EAAE,CAAC;IACxD,CAAC;IAED,MAAM;QACJ,MAAM,EAAE,MAAM,EAAE,GAAG,KAAK,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;QACxC,MAAM,QAAQ,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;QAErC,OAAO,CAAC,eAAe,CAAC,IAAI,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,EAAG,CAAC;IAC/E,CAAC;CACF;AAED,gFAAgF;AAChF,gEAAgE;AAChE,yEAAyE;AACzE,SAAS,WAAW,CAAC,MAA4B;IAC/C,IAAI,MAAM,YAAY,iBAAiB,CAAC,WAAW,EAAE;QACnD,mBAAmB;QACnB,OAAO,MAAM,CAAC,yBAAyB,CAAC;KACzC;IACD,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE;QAC9B,OAAO,MAAM,CAAC;KACf;IACD,OAAO,IAAI,CAAC;AACd,CAAC","sourcesContent":["import { ReactNode, PureComponent, createRef } from 'react';\n\nimport NativeVideoModule from './NativeVideoModule';\nimport NativeVideoView from './NativeVideoView';\nimport type { VideoPlayer, VideoViewProps } from './VideoView.types';\n\n/**\n * Returns whether the current device supports Picture in Picture (PiP) mode.\n * @returns A `boolean` which is `true` if the device supports PiP mode, and `false` otherwise.\n * @platform android\n * @platform ios\n */\nexport function isPictureInPictureSupported(): boolean {\n return NativeVideoModule.isPictureInPictureSupported();\n}\n\nexport class VideoView extends PureComponent<VideoViewProps> {\n nativeRef = createRef<any>();\n\n /**\n * Enters fullscreen mode.\n */\n enterFullscreen() {\n this.nativeRef.current?.enterFullscreen();\n }\n\n /**\n * Exits fullscreen mode.\n */\n exitFullscreen() {\n this.nativeRef.current?.exitFullscreen();\n }\n\n /**\n * Enters Picture in Picture (PiP) mode. Throws an exception if the device does not support PiP.\n * > **Note:** Only one player can be in Picture in Picture (PiP) mode at a time.\n * @platform android\n * @platform ios 14+\n */\n startPictureInPicture(): void {\n return this.nativeRef.current?.startPictureInPicture();\n }\n\n /**\n * Exits Picture in Picture (PiP) mode.\n * @platform android\n * @platform ios 14+\n */\n stopPictureInPicture(): void {\n return this.nativeRef.current?.stopPictureInPicture();\n }\n\n render(): ReactNode {\n const { player, ...props } = this.props;\n const playerId = getPlayerId(player);\n\n return <NativeVideoView {...props} player={playerId} ref={this.nativeRef} />;\n }\n}\n\n// Temporary solution to pass the shared object ID instead of the player object.\n// We can't really pass it as an object in the old architecture.\n// Technically we can in the new architecture, but it's not possible yet.\nfunction getPlayerId(player: number | VideoPlayer): number | null {\n if (player instanceof NativeVideoModule.VideoPlayer) {\n // @ts-expect-error\n return player.__expo_shared_object_id__;\n }\n if (typeof player === 'number') {\n return player;\n }\n return null;\n}\n"]}
|