expo-video 1.1.9 → 1.2.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 +27 -0
- package/android/build.gradle +5 -3
- package/android/src/main/AndroidManifest.xml +1 -0
- package/android/src/main/java/expo/modules/video/ContentFit.kt +2 -2
- package/android/src/main/java/expo/modules/video/DataSourceUtils.kt +44 -0
- package/android/src/main/java/expo/modules/video/VideoManager.kt +4 -17
- package/android/src/main/java/expo/modules/video/VideoModule.kt +20 -10
- package/android/src/main/java/expo/modules/video/VideoPlayer.kt +19 -16
- package/android/src/main/java/expo/modules/video/records/VideoSource.kt +15 -4
- package/build/VideoPlayer.types.d.ts +18 -0
- package/build/VideoPlayer.types.d.ts.map +1 -1
- package/build/VideoPlayer.types.js.map +1 -1
- package/build/VideoPlayer.web.d.ts +1 -0
- package/build/VideoPlayer.web.d.ts.map +1 -1
- package/build/VideoPlayer.web.js +4 -0
- package/build/VideoPlayer.web.js.map +1 -1
- package/build/VideoView.web.js +1 -1
- package/build/VideoView.web.js.map +1 -1
- package/ios/ContentKeyDelegate.swift +15 -1
- package/ios/NowPlayingManager.swift +36 -22
- package/ios/Records/DRMOptions.swift +3 -3
- package/ios/Records/VideoSource.swift +3 -0
- package/ios/VideoModule.swift +12 -4
- package/ios/VideoPlayer.swift +2 -2
- package/ios/VideoPlayerObserver.swift +46 -17
- package/package.json +2 -2
- package/src/VideoPlayer.types.ts +20 -0
- package/src/VideoPlayer.web.tsx +5 -0
- package/src/VideoView.web.tsx +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,33 @@
|
|
|
10
10
|
|
|
11
11
|
### 💡 Others
|
|
12
12
|
|
|
13
|
+
## 1.2.0 — 2024-06-20
|
|
14
|
+
|
|
15
|
+
### 🎉 New features
|
|
16
|
+
|
|
17
|
+
- Add `isLive` property on all platforms. ([#28903](https://github.com/expo/expo/pull/28903) by [@justjoostnl](https://github.com/justjoostnl))
|
|
18
|
+
- [iOS] Add base64 certificate support for FairPlay DRM. ([#28990](https://github.com/expo/expo/pull/28990) by [@behenate](https://github.com/behenate))
|
|
19
|
+
- [Android][iOS] Add support for request headers to in the video source. ([#29539](https://github.com/expo/expo/pull/29539) by [@behenate](https://github.com/behenate))
|
|
20
|
+
|
|
21
|
+
### 🐛 Bug fixes
|
|
22
|
+
|
|
23
|
+
- [Android] Fix wrong content fit "fill" and "cover". ([#29364](https://github.com/expo/expo/pull/29364) by [@RRaideRR](https://github.com/RRaideRR))
|
|
24
|
+
- [iOS] Fix player status property always returning `undefined` on iOS. ([#29505](https://github.com/expo/expo/pull/29505) by [@behenate](https://github.com/behenate))
|
|
25
|
+
- [Android] Fix `VideoPlayer.replace` not working when the previous source caused an error. ([#29598](https://github.com/expo/expo/pull/29598) by [@lukmccall](https://github.com/lukmccall))
|
|
26
|
+
- [Web] Fix default behavior for `nativeControls` to match documentation. ([#29667](https://github.com/expo/expo/pull/29667) by [@nahn20](https://github.com/nahn20))
|
|
27
|
+
- [iOS] Fix crashes when creating new players. ([#29428](https://github.com/expo/expo/pull/29428) by [@behenate](https://github.com/behenate))
|
|
28
|
+
- Fix errors on setting a null video source. ([#29613](https://github.com/expo/expo/pull/29613) by [@behenate](https://github.com/behenate))
|
|
29
|
+
|
|
30
|
+
### 💡 Others
|
|
31
|
+
|
|
32
|
+
- [iOS] Make appropriate references weak in `VideoPlayerObserver`. ([#29427](https://github.com/expo/expo/pull/29427) by [@behenate](https://github.com/behenate))
|
|
33
|
+
|
|
34
|
+
## 1.1.10 — 2024-05-29
|
|
35
|
+
|
|
36
|
+
### 💡 Others
|
|
37
|
+
|
|
38
|
+
- [Android] Improve HLS compatibility. ([#28997](https://github.com/expo/expo/pull/28997) by [@behenate](https://github.com/behenate))
|
|
39
|
+
|
|
13
40
|
## 1.1.9 — 2024-05-13
|
|
14
41
|
|
|
15
42
|
### 🎉 New features
|
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.
|
|
4
|
+
version = '1.2.0'
|
|
5
5
|
|
|
6
6
|
def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
|
|
7
7
|
apply from: expoModulesCorePlugin
|
|
@@ -14,18 +14,20 @@ android {
|
|
|
14
14
|
namespace "expo.modules.video"
|
|
15
15
|
defaultConfig {
|
|
16
16
|
versionCode 1
|
|
17
|
-
versionName '1.
|
|
17
|
+
versionName '1.2.0'
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
dependencies {
|
|
22
22
|
implementation 'com.facebook.react:react-android'
|
|
23
23
|
|
|
24
|
-
def androidxMedia3Version = "1.
|
|
24
|
+
def androidxMedia3Version = "1.3.1"
|
|
25
25
|
implementation "androidx.media3:media3-session:${androidxMedia3Version}"
|
|
26
26
|
implementation "androidx.media3:media3-exoplayer:${androidxMedia3Version}"
|
|
27
27
|
implementation "androidx.media3:media3-exoplayer-dash:${androidxMedia3Version}"
|
|
28
|
+
implementation "androidx.media3:media3-exoplayer-hls:${androidxMedia3Version}"
|
|
28
29
|
implementation "androidx.media3:media3-ui:${androidxMedia3Version}"
|
|
30
|
+
implementation "androidx.media3:media3-datasource-okhttp:${androidxMedia3Version}"
|
|
29
31
|
|
|
30
32
|
def fragment_version = "1.6.2"
|
|
31
33
|
implementation "androidx.fragment:fragment:$fragment_version"
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
|
2
|
+
<uses-permission android:name="android.permission.INTERNET" />
|
|
2
3
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
|
3
4
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
|
|
4
5
|
|
|
@@ -12,8 +12,8 @@ enum class ContentFit(val value: String) : Enumerable {
|
|
|
12
12
|
fun toResizeMode(): Int {
|
|
13
13
|
return when (this) {
|
|
14
14
|
CONTAIN -> AspectRatioFrameLayout.RESIZE_MODE_FIT
|
|
15
|
-
FILL -> AspectRatioFrameLayout.
|
|
16
|
-
COVER -> AspectRatioFrameLayout.
|
|
15
|
+
FILL -> AspectRatioFrameLayout.RESIZE_MODE_FILL
|
|
16
|
+
COVER -> AspectRatioFrameLayout.RESIZE_MODE_ZOOM
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
19
|
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
package expo.modules.video
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.content.pm.ApplicationInfo
|
|
5
|
+
import androidx.annotation.OptIn
|
|
6
|
+
import androidx.media3.common.util.UnstableApi
|
|
7
|
+
import androidx.media3.common.util.Util
|
|
8
|
+
import androidx.media3.datasource.okhttp.OkHttpDataSource
|
|
9
|
+
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory
|
|
10
|
+
import androidx.media3.exoplayer.source.MediaSource
|
|
11
|
+
import expo.modules.video.records.VideoSource
|
|
12
|
+
import okhttp3.OkHttpClient
|
|
13
|
+
|
|
14
|
+
@OptIn(UnstableApi::class)
|
|
15
|
+
fun buildDataSourceFactory(context: Context, videoSource: VideoSource): OkHttpDataSource.Factory {
|
|
16
|
+
val client = OkHttpClient.Builder().build()
|
|
17
|
+
val userAgent = Util.getUserAgent(context, getApplicationName(context))
|
|
18
|
+
return OkHttpDataSource.Factory(client).apply {
|
|
19
|
+
videoSource.headers?.let {
|
|
20
|
+
if (it.isNotEmpty()) {
|
|
21
|
+
setDefaultRequestProperties(it)
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
setUserAgent(userAgent)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
fun buildMediaSourceFactory(context: Context, dataSourceFactory: OkHttpDataSource.Factory): MediaSource.Factory {
|
|
29
|
+
return DefaultMediaSourceFactory(context).setDataSourceFactory(dataSourceFactory)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
@OptIn(UnstableApi::class)
|
|
33
|
+
fun buildMediaSourceWithHeaders(context: Context, videoSource: VideoSource): MediaSource {
|
|
34
|
+
val dataSourceFactory = buildDataSourceFactory(context, videoSource)
|
|
35
|
+
val mediaSourceFactory = buildMediaSourceFactory(context, dataSourceFactory)
|
|
36
|
+
val mediaItem = videoSource.toMediaItem()
|
|
37
|
+
return mediaSourceFactory.createMediaSource(mediaItem)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
private fun getApplicationName(context: Context): String {
|
|
41
|
+
val applicationInfo: ApplicationInfo = context.applicationInfo
|
|
42
|
+
val stringId = applicationInfo.labelRes
|
|
43
|
+
return if (stringId == 0) applicationInfo.nonLocalizedLabel.toString() else context.getString(stringId)
|
|
44
|
+
}
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
package expo.modules.video
|
|
2
2
|
|
|
3
3
|
import androidx.annotation.OptIn
|
|
4
|
-
import androidx.media3.common.MediaItem
|
|
5
4
|
import androidx.media3.common.util.UnstableApi
|
|
6
|
-
import expo.modules.video.records.VideoSource
|
|
7
|
-
import java.lang.ref.WeakReference
|
|
8
5
|
|
|
9
6
|
// Helper class used to keep track of all existing VideoViews and VideoPlayers
|
|
10
7
|
@OptIn(UnstableApi::class)
|
|
@@ -17,9 +14,6 @@ object VideoManager {
|
|
|
17
14
|
// Keeps track of all existing VideoPlayers, and whether they are attached to a VideoView
|
|
18
15
|
private var videoPlayersToVideoViews = mutableMapOf<VideoPlayer, MutableList<VideoView>>()
|
|
19
16
|
|
|
20
|
-
// Keeps track of all existing MediaItems and their corresponding VideoSources. Used for recognizing source of MediaItems.
|
|
21
|
-
private var mediaItemsToVideoSources = mutableMapOf<String, WeakReference<VideoSource>>()
|
|
22
|
-
|
|
23
17
|
fun registerVideoView(videoView: VideoView) {
|
|
24
18
|
videoViews[videoView.id] = videoView
|
|
25
19
|
}
|
|
@@ -40,17 +34,6 @@ object VideoManager {
|
|
|
40
34
|
videoPlayersToVideoViews.remove(videoPlayer)
|
|
41
35
|
}
|
|
42
36
|
|
|
43
|
-
fun registerVideoSourceToMediaItem(mediaItem: MediaItem, videoSource: VideoSource) {
|
|
44
|
-
mediaItemsToVideoSources[mediaItem.mediaId] = WeakReference(videoSource)
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
fun getVideoSourceFromMediaItem(mediaItem: MediaItem?): VideoSource? {
|
|
48
|
-
if (mediaItem == null) {
|
|
49
|
-
return null
|
|
50
|
-
}
|
|
51
|
-
return mediaItemsToVideoSources[mediaItem.mediaId]?.get()
|
|
52
|
-
}
|
|
53
|
-
|
|
54
37
|
fun onVideoPlayerAttachedToView(videoPlayer: VideoPlayer, videoView: VideoView) {
|
|
55
38
|
if (videoPlayersToVideoViews[videoPlayer]?.contains(videoView) == true) {
|
|
56
39
|
return
|
|
@@ -73,6 +56,10 @@ object VideoManager {
|
|
|
73
56
|
}
|
|
74
57
|
}
|
|
75
58
|
|
|
59
|
+
fun isVideoPlayerAttachedToView(videoPlayer: VideoPlayer): Boolean {
|
|
60
|
+
return videoPlayersToVideoViews[videoPlayer]?.isNotEmpty() ?: false
|
|
61
|
+
}
|
|
62
|
+
|
|
76
63
|
fun onAppForegrounded() = Unit
|
|
77
64
|
|
|
78
65
|
fun onAppBackgrounded() {
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
package expo.modules.video
|
|
4
4
|
|
|
5
5
|
import android.app.Activity
|
|
6
|
+
import android.content.Context
|
|
6
7
|
import androidx.media3.common.PlaybackParameters
|
|
7
8
|
import androidx.media3.common.Player.REPEAT_MODE_OFF
|
|
8
9
|
import androidx.media3.common.Player.REPEAT_MODE_ONE
|
|
@@ -24,6 +25,8 @@ import kotlinx.coroutines.runBlocking
|
|
|
24
25
|
class VideoModule : Module() {
|
|
25
26
|
private val activity: Activity
|
|
26
27
|
get() = appContext.activityProvider?.currentActivity ?: throw Exceptions.MissingActivity()
|
|
28
|
+
private val reactContext: Context
|
|
29
|
+
get() = appContext.reactContext ?: throw Exceptions.ReactContextLost()
|
|
27
30
|
|
|
28
31
|
override fun definition() = ModuleDefinition {
|
|
29
32
|
Name("ExpoVideo")
|
|
@@ -138,7 +141,7 @@ class VideoModule : Module() {
|
|
|
138
141
|
}
|
|
139
142
|
|
|
140
143
|
Class(VideoPlayer::class) {
|
|
141
|
-
Constructor { source: VideoSource ->
|
|
144
|
+
Constructor { source: VideoSource? ->
|
|
142
145
|
VideoPlayer(activity.applicationContext, appContext, source)
|
|
143
146
|
}
|
|
144
147
|
|
|
@@ -199,6 +202,11 @@ class VideoModule : Module() {
|
|
|
199
202
|
}
|
|
200
203
|
}
|
|
201
204
|
|
|
205
|
+
Property("isLive")
|
|
206
|
+
.get { ref: VideoPlayer ->
|
|
207
|
+
ref.isLive
|
|
208
|
+
}
|
|
209
|
+
|
|
202
210
|
Property("preservesPitch")
|
|
203
211
|
.get { ref: VideoPlayer ->
|
|
204
212
|
ref.preservesPitch
|
|
@@ -258,18 +266,20 @@ class VideoModule : Module() {
|
|
|
258
266
|
}
|
|
259
267
|
}
|
|
260
268
|
|
|
261
|
-
Function("replace") { ref: VideoPlayer, source: Either<String, VideoSource
|
|
262
|
-
val videoSource =
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
269
|
+
Function("replace") { ref: VideoPlayer, source: Either<String, VideoSource>? ->
|
|
270
|
+
val videoSource = source?.let {
|
|
271
|
+
if (it.`is`(VideoSource::class)) {
|
|
272
|
+
it.get(VideoSource::class)
|
|
273
|
+
} else {
|
|
274
|
+
VideoSource(it.get(String::class))
|
|
275
|
+
}
|
|
266
276
|
}
|
|
267
|
-
val mediaItem = videoSource.toMediaItem()
|
|
268
|
-
VideoManager.registerVideoSourceToMediaItem(mediaItem, videoSource)
|
|
269
277
|
|
|
270
278
|
appContext.mainQueue.launch {
|
|
271
|
-
ref.
|
|
272
|
-
|
|
279
|
+
ref.uncommittedSource = videoSource
|
|
280
|
+
if (VideoManager.isVideoPlayerAttachedToView(ref)) {
|
|
281
|
+
ref.prepare()
|
|
282
|
+
}
|
|
273
283
|
}
|
|
274
284
|
}
|
|
275
285
|
|
|
@@ -31,7 +31,7 @@ import java.lang.ref.WeakReference
|
|
|
31
31
|
|
|
32
32
|
// https://developer.android.com/guide/topics/media/media3/getting-started/migration-guide#improvements_in_media3
|
|
33
33
|
@UnstableApi
|
|
34
|
-
class VideoPlayer(context: Context, appContext: AppContext, source: VideoSource?) : AutoCloseable, SharedObject(appContext) {
|
|
34
|
+
class VideoPlayer(val context: Context, appContext: AppContext, source: VideoSource?) : AutoCloseable, SharedObject(appContext) {
|
|
35
35
|
// This improves the performance of playing DRM-protected content
|
|
36
36
|
private var renderersFactory = DefaultRenderersFactory(context)
|
|
37
37
|
.forceEnableMediaCodecAsynchronousQueueing()
|
|
@@ -50,14 +50,13 @@ class VideoPlayer(context: Context, appContext: AppContext, source: VideoSource?
|
|
|
50
50
|
field = value
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
sendEventOnJSThread("sourceChange", videoSource, field)
|
|
53
|
+
var uncommittedSource: VideoSource? = source
|
|
54
|
+
private var lastLoadedSource: VideoSource? = null
|
|
55
|
+
set(value) {
|
|
56
|
+
if (field != value && value != null) {
|
|
57
|
+
sendEventOnJSThread("sourceChange", value, field)
|
|
59
58
|
}
|
|
60
|
-
field =
|
|
59
|
+
field = value
|
|
61
60
|
}
|
|
62
61
|
|
|
63
62
|
// Volume of the player if there was no mute applied.
|
|
@@ -76,6 +75,7 @@ class VideoPlayer(context: Context, appContext: AppContext, source: VideoSource?
|
|
|
76
75
|
playbackServiceBinder?.service?.setShowNotification(value, this.player)
|
|
77
76
|
}
|
|
78
77
|
var duration = 0f
|
|
78
|
+
var isLive = false
|
|
79
79
|
|
|
80
80
|
private var serviceConnection: ServiceConnection
|
|
81
81
|
internal var playbackServiceBinder: PlaybackServiceBinder? = null
|
|
@@ -122,9 +122,8 @@ class VideoPlayer(context: Context, appContext: AppContext, source: VideoSource?
|
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
override fun onMediaItemTransition(mediaItem: MediaItem?, reason: Int) {
|
|
125
|
-
val newVideoSource = VideoManager.getVideoSourceFromMediaItem(mediaItem)
|
|
126
|
-
this@VideoPlayer.videoSource = newVideoSource
|
|
127
125
|
this@VideoPlayer.duration = 0f
|
|
126
|
+
this@VideoPlayer.isLive = false
|
|
128
127
|
if (reason == Player.MEDIA_ITEM_TRANSITION_REASON_REPEAT) {
|
|
129
128
|
sendEventOnJSThread("playToEnd")
|
|
130
129
|
}
|
|
@@ -137,6 +136,7 @@ class VideoPlayer(context: Context, appContext: AppContext, source: VideoSource?
|
|
|
137
136
|
}
|
|
138
137
|
if (playbackState == Player.STATE_READY) {
|
|
139
138
|
this@VideoPlayer.duration = this@VideoPlayer.player.duration / 1000f
|
|
139
|
+
this@VideoPlayer.isLive = this@VideoPlayer.player.isCurrentMediaItemLive
|
|
140
140
|
}
|
|
141
141
|
setStatus(playerStateToPlayerStatus(playbackState), null)
|
|
142
142
|
super.onPlaybackStateChanged(playbackState)
|
|
@@ -156,6 +156,7 @@ class VideoPlayer(context: Context, appContext: AppContext, source: VideoSource?
|
|
|
156
156
|
error?.let {
|
|
157
157
|
setStatus(ERROR, error)
|
|
158
158
|
this@VideoPlayer.duration = 0f
|
|
159
|
+
this@VideoPlayer.isLive = false
|
|
159
160
|
} ?: run {
|
|
160
161
|
setStatus(playerStateToPlayerStatus(player.playbackState), null)
|
|
161
162
|
}
|
|
@@ -218,7 +219,8 @@ class VideoPlayer(context: Context, appContext: AppContext, source: VideoSource?
|
|
|
218
219
|
player.removeListener(playerListener)
|
|
219
220
|
player.release()
|
|
220
221
|
}
|
|
221
|
-
|
|
222
|
+
uncommittedSource = null
|
|
223
|
+
lastLoadedSource = null
|
|
222
224
|
}
|
|
223
225
|
|
|
224
226
|
override fun deallocate() {
|
|
@@ -233,13 +235,14 @@ class VideoPlayer(context: Context, appContext: AppContext, source: VideoSource?
|
|
|
233
235
|
}
|
|
234
236
|
|
|
235
237
|
fun prepare() {
|
|
236
|
-
|
|
237
|
-
val
|
|
238
|
-
|
|
239
|
-
player.setMediaItem(mediaItem)
|
|
238
|
+
uncommittedSource?.let { videoSource ->
|
|
239
|
+
val mediaSource = videoSource.toMediaSource(context)
|
|
240
|
+
player.setMediaSource(mediaSource)
|
|
240
241
|
player.prepare()
|
|
242
|
+
lastLoadedSource = videoSource
|
|
243
|
+
uncommittedSource = null
|
|
241
244
|
} ?: run {
|
|
242
|
-
player.
|
|
245
|
+
player.clearMediaItems()
|
|
243
246
|
player.prepare()
|
|
244
247
|
}
|
|
245
248
|
}
|
|
@@ -1,28 +1,39 @@
|
|
|
1
1
|
package expo.modules.video.records
|
|
2
|
-
|
|
2
|
+
import android.content.Context
|
|
3
|
+
import androidx.annotation.OptIn
|
|
3
4
|
import androidx.media3.common.MediaItem
|
|
4
5
|
import androidx.media3.common.MediaMetadata
|
|
6
|
+
import androidx.media3.common.util.UnstableApi
|
|
7
|
+
import androidx.media3.exoplayer.source.MediaSource
|
|
5
8
|
import expo.modules.kotlin.records.Field
|
|
6
9
|
import expo.modules.kotlin.records.Record
|
|
7
10
|
import expo.modules.video.UnsupportedDRMTypeException
|
|
11
|
+
import expo.modules.video.buildMediaSourceWithHeaders
|
|
8
12
|
import java.io.Serializable
|
|
9
13
|
|
|
14
|
+
@OptIn(UnstableApi::class)
|
|
10
15
|
class VideoSource(
|
|
11
16
|
@Field var uri: String? = null,
|
|
12
17
|
@Field var drm: DRMOptions? = null,
|
|
13
|
-
@Field var metadata: VideoMetadata? = null
|
|
18
|
+
@Field var metadata: VideoMetadata? = null,
|
|
19
|
+
@Field var headers: Map<String, String>? = null
|
|
14
20
|
) : Record, Serializable {
|
|
15
21
|
private fun toMediaId(): String {
|
|
16
22
|
return "uri:${this.uri}" +
|
|
23
|
+
"Headers: ${this.headers}" +
|
|
17
24
|
"DrmType:${this.drm?.type}" +
|
|
18
25
|
"DrmLicenseServer:${this.drm?.licenseServer}" +
|
|
19
26
|
"DrmMultiKey:${this.drm?.multiKey}" +
|
|
20
|
-
"DRMHeadersKeys:${this.drm?.headers?.keys?.joinToString {it}}}" +
|
|
21
|
-
"DRMHeadersValues:${this.drm?.headers?.values?.joinToString {it}}}" +
|
|
27
|
+
"DRMHeadersKeys:${this.drm?.headers?.keys?.joinToString { it }}}" +
|
|
28
|
+
"DRMHeadersValues:${this.drm?.headers?.values?.joinToString { it }}}" +
|
|
22
29
|
"NotificationDataTitle:${this.metadata?.title}" +
|
|
23
30
|
"NotificationDataSecondaryText:${this.metadata?.artist}"
|
|
24
31
|
}
|
|
25
32
|
|
|
33
|
+
fun toMediaSource(context: Context): MediaSource {
|
|
34
|
+
return buildMediaSourceWithHeaders(context, this)
|
|
35
|
+
}
|
|
36
|
+
|
|
26
37
|
fun toMediaItem() = MediaItem
|
|
27
38
|
.Builder()
|
|
28
39
|
.apply {
|
|
@@ -53,6 +53,11 @@ export declare class VideoPlayer extends SharedObject<VideoPlayerEvents> {
|
|
|
53
53
|
* @default 1.0
|
|
54
54
|
*/
|
|
55
55
|
playbackRate: number;
|
|
56
|
+
/**
|
|
57
|
+
* Boolean value indicating whether the player is currently playing a live stream.
|
|
58
|
+
* > This property is get-only
|
|
59
|
+
*/
|
|
60
|
+
isLive: boolean;
|
|
56
61
|
/**
|
|
57
62
|
* Indicates the current status of the player.
|
|
58
63
|
* > This property is get-only
|
|
@@ -146,6 +151,13 @@ export type VideoSource = string | {
|
|
|
146
151
|
* When undefined the player will display information contained in the video metadata.
|
|
147
152
|
*/
|
|
148
153
|
metadata?: VideoMetadata;
|
|
154
|
+
/**
|
|
155
|
+
* Specifies headers sent with the video request.
|
|
156
|
+
* > For DRM license headers use the `headers` field of [`DRMOptions`](#drmoptions).
|
|
157
|
+
* @platform android
|
|
158
|
+
* @platform ios
|
|
159
|
+
*/
|
|
160
|
+
headers?: Record<string, string>;
|
|
149
161
|
} | null;
|
|
150
162
|
/**
|
|
151
163
|
* Contains information about any errors that the player encountered during the playback
|
|
@@ -210,5 +222,11 @@ export type DRMOptions = {
|
|
|
210
222
|
* @platform ios
|
|
211
223
|
*/
|
|
212
224
|
certificateUrl?: string;
|
|
225
|
+
/**
|
|
226
|
+
* Specifies the base64 encoded certificate data for the FairPlay DRM.
|
|
227
|
+
* When this property is set, the `certificateUrl` property is ignored.
|
|
228
|
+
* @platform ios
|
|
229
|
+
*/
|
|
230
|
+
base64CertificateData?: string;
|
|
213
231
|
};
|
|
214
232
|
//# sourceMappingURL=VideoPlayer.types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VideoPlayer.types.d.ts","sourceRoot":"","sources":["../src/VideoPlayer.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEtD;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,WAAY,SAAQ,YAAY,CAAC,iBAAiB,CAAC;IACtE;;;OAGG;IACH,OAAO,EAAE,OAAO,CAAC;IAEjB;;;OAGG;IACH,IAAI,EAAE,OAAO,CAAC;IAEd;;;;OAIG;IACH,KAAK,EAAE,OAAO,CAAC;IAEf;;;;;;;OAOG;IACH,WAAW,EAAE,MAAM,CAAC;IAEpB;;;OAGG;IACH,QAAQ,EAAE,MAAM,CAAC;IAEjB;;;;;OAKG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;;;;;OAMG;IACH,cAAc,EAAE,OAAO,CAAC;IAExB;;;OAGG;IACH,YAAY,EAAE,MAAM,CAAC;IAErB;;;OAGG;IACH,MAAM,EAAE,iBAAiB,CAAC;IAE1B;;OAEG;IACH,0BAA0B,EAAE,OAAO,CAAC;IAEpC;;;;;OAKG;IACH,uBAAuB,EAAE,OAAO,CAAC;IAEjC;;;OAGG;gBACS,MAAM,EAAE,WAAW;IAE/B;;OAEG;IACH,IAAI,IAAI,IAAI;IAEZ;;OAEG;IACH,KAAK,IAAI,IAAI;IAEb;;OAEG;IACH,OAAO,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI;IAElC;;OAEG;IACH,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAE7B;;OAEG;IACH,MAAM,IAAI,IAAI;CACf;AAED;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B;;OAEG;IACH,YAAY,CACV,SAAS,EAAE,iBAAiB,EAC5B,SAAS,EAAE,iBAAiB,EAC5B,KAAK,EAAE,WAAW,GACjB,IAAI,CAAC;IACR;;OAEG;IACH,aAAa,CAAC,YAAY,EAAE,OAAO,EAAE,YAAY,EAAE,OAAO,GAAG,IAAI,CAAC;IAClE;;OAEG;IACH,kBAAkB,CAAC,eAAe,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3E;;OAEG;IACH,YAAY,CAAC,SAAS,EAAE,WAAW,EAAE,SAAS,EAAE,WAAW,GAAG,IAAI,CAAC;IACnE;;OAEG;IACH,SAAS,IAAI,IAAI,CAAC;IAClB;;OAEG;IACH,YAAY,CAAC,SAAS,EAAE,WAAW,EAAE,cAAc,EAAE,WAAW,GAAG,IAAI,CAAC;CACzE,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,SAAS,GAAG,aAAa,GAAG,OAAO,CAAC;AAE7E,MAAM,MAAM,WAAW,GACnB,MAAM,GACN;IACE;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ;;OAEG;IACH,GAAG,CAAC,EAAE,UAAU,CAAC;IACjB;;;OAGG;IACH,QAAQ,CAAC,EAAE,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"VideoPlayer.types.d.ts","sourceRoot":"","sources":["../src/VideoPlayer.types.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VideoPlayer.types.js","sourceRoot":"","sources":["../src/VideoPlayer.types.ts"],"names":[],"mappings":"","sourcesContent":["import type { SharedObject } from 'expo-modules-core';\n\n/**\n * A class that represents an instance of the video player.\n */\nexport declare class VideoPlayer extends SharedObject<VideoPlayerEvents> {\n /**\n * Boolean value whether the player is currently playing.\n * > This property is get-only, use `play` and `pause` methods to control the playback.\n */\n playing: boolean;\n\n /**\n * Determines whether the player should automatically replay after reaching the end of the video.\n * @default false\n */\n loop: boolean;\n\n /**\n * Boolean value whether the player is currently muted.\n * Setting this property to `true`/`false` will mute/unmute the player.\n * @default false\n */\n muted: boolean;\n\n /**\n * Float value indicating the current playback time in seconds.\n *\n * If the player is not yet playing, this value indicates the time position\n * at which playback will begin once the `play()` method is called.\n *\n * Setting `currentTime` to a new value seeks the player to the given time.\n */\n currentTime: number;\n\n /**\n * Float value indicating the duration of the current video in seconds.\n * > This property is get-only\n */\n duration: number;\n\n /**\n * Float value between 0 and 1 representing the current volume.\n * Muting the player doesn't affect the volume. In other words, when the player is muted, the volume is the same as\n * when unmuted. Similarly, setting the volume doesn't unmute the player.\n * @default 1.0\n */\n volume: number;\n\n /**\n * Boolean value indicating if the player should correct audio pitch when the playback speed changes.\n * > On web, changing this property is not supported, the player will always correct the pitch.\n * @default true\n * @platform android\n * @platform ios\n */\n preservesPitch: boolean;\n\n /**\n * Float value between 0 and 16 indicating the current playback speed of the player.\n * @default 1.0\n */\n playbackRate: number;\n\n /**\n * Indicates the current status of the player.\n * > This property is get-only\n */\n status: VideoPlayerStatus;\n\n /**\n * Boolean value determining whether the player should show the now playing notification.\n */\n showNowPlayingNotification: boolean;\n\n /**\n * Determines whether the player should continue playing after the app enters the background.\n * @default false\n * @platform ios\n * @platform android\n */\n staysActiveInBackground: boolean;\n\n /**\n * Initializes a new video player instance with the given source.\n * @hidden\n */\n constructor(source: VideoSource);\n\n /**\n * Resumes the player.\n */\n play(): void;\n\n /**\n * Pauses the player.\n */\n pause(): void;\n\n /**\n * Replaces the current source with a new one.\n */\n replace(source: VideoSource): void;\n\n /**\n * Seeks the playback by the given number of seconds.\n */\n seekBy(seconds: number): void;\n\n /**\n * Seeks the playback to the beginning.\n */\n replay(): void;\n}\n\n/**\n * Handlers for events which can be emitted by the player.\n */\nexport type VideoPlayerEvents = {\n /**\n * Handler for an event emitted when the status of the player changes.\n */\n statusChange(\n newStatus: VideoPlayerStatus,\n oldStatus: VideoPlayerStatus,\n error: PlayerError\n ): void;\n /**\n * Handler for an event emitted when the player starts or stops playback.\n */\n playingChange(newIsPlaying: boolean, oldIsPlaying: boolean): void;\n /**\n * Handler for an event emitted when the `playbackRate` property of the player changes.\n */\n playbackRateChange(newPlaybackRate: number, oldPlaybackRate: number): void;\n /**\n * Handler for an event emitted when the `volume` property of the player changes.\n */\n volumeChange(newVolume: VolumeEvent, oldVolume: VolumeEvent): void;\n /**\n * Handler for an event emitted when the player plays to the end of the current source.\n */\n playToEnd(): void;\n /**\n * Handler for an event emitted when the current media source of the player changes.\n */\n sourceChange(newSource: VideoSource, previousSource: VideoSource): void;\n};\n\n/**\n * Describes the current status of the player.\n * - `idle`: The player is not playing or loading any videos.\n * - `loading`: The player is loading video data from the provided source\n * - `readyToPlay`: The player has loaded enough data to start playing or to continue playback.\n * - `error`: The player has encountered an error while loading or playing the video.\n */\nexport type VideoPlayerStatus = 'idle' | 'loading' | 'readyToPlay' | 'error';\n\nexport type VideoSource =\n | string\n | {\n /**\n * The URI of the video.\n */\n uri: string;\n /**\n * Specifies the DRM options which will be used by the player while loading the video.\n */\n drm?: DRMOptions;\n /**\n * Specifies information which will be displayed in the now playing notification.\n * When undefined the player will display information contained in the video metadata.\n */\n metadata?: VideoMetadata;\n }\n | null;\n\n/**\n * Contains information about any errors that the player encountered during the playback\n */\nexport type PlayerError = {\n message: string;\n};\n\n/**\n * Contains information about the current volume and whether the player is muted.\n */\nexport type VolumeEvent = {\n volume: number;\n isMuted: boolean;\n};\n\n/**\n * Contains information that will be displayed in the now playing notification when the video is playing.\n */\nexport type VideoMetadata = {\n /**\n * The title of the video.\n */\n title?: string;\n /**\n * Secondary text that will be displayed under the title.\n */\n artist?: string;\n};\n\n/**\n * Specifies which type of DRM to use. Android supports Widevine, PlayReady and ClearKey, iOS supports FairPlay.\n */\nexport type DRMType = 'clearkey' | 'fairplay' | 'playready' | 'widevine';\n\n/**\n * Specifies DRM options which will be used by the player while loading the video.\n */\nexport type DRMOptions = {\n /**\n * Determines which type of DRM to use.\n */\n type: DRMType;\n\n /**\n * Determines the license server URL.\n */\n licenseServer: string;\n\n /**\n * Determines headers sent to the license server on license requests.\n */\n headers?: { [key: string]: string };\n\n /**\n * Specifies whether the DRM is a multi-key DRM.\n * @platform android\n */\n multiKey?: boolean;\n\n /**\n * Specifies the content ID of the stream.\n * @platform ios\n */\n contentId?: string;\n\n /**\n * Specifies the certificate URL for the FairPlay DRM.\n * @platform ios\n */\n certificateUrl?: string;\n};\n"]}
|
|
1
|
+
{"version":3,"file":"VideoPlayer.types.js","sourceRoot":"","sources":["../src/VideoPlayer.types.ts"],"names":[],"mappings":"","sourcesContent":["import type { SharedObject } from 'expo-modules-core';\n\n/**\n * A class that represents an instance of the video player.\n */\nexport declare class VideoPlayer extends SharedObject<VideoPlayerEvents> {\n /**\n * Boolean value whether the player is currently playing.\n * > This property is get-only, use `play` and `pause` methods to control the playback.\n */\n playing: boolean;\n\n /**\n * Determines whether the player should automatically replay after reaching the end of the video.\n * @default false\n */\n loop: boolean;\n\n /**\n * Boolean value whether the player is currently muted.\n * Setting this property to `true`/`false` will mute/unmute the player.\n * @default false\n */\n muted: boolean;\n\n /**\n * Float value indicating the current playback time in seconds.\n *\n * If the player is not yet playing, this value indicates the time position\n * at which playback will begin once the `play()` method is called.\n *\n * Setting `currentTime` to a new value seeks the player to the given time.\n */\n currentTime: number;\n\n /**\n * Float value indicating the duration of the current video in seconds.\n * > This property is get-only\n */\n duration: number;\n\n /**\n * Float value between 0 and 1 representing the current volume.\n * Muting the player doesn't affect the volume. In other words, when the player is muted, the volume is the same as\n * when unmuted. Similarly, setting the volume doesn't unmute the player.\n * @default 1.0\n */\n volume: number;\n\n /**\n * Boolean value indicating if the player should correct audio pitch when the playback speed changes.\n * > On web, changing this property is not supported, the player will always correct the pitch.\n * @default true\n * @platform android\n * @platform ios\n */\n preservesPitch: boolean;\n\n /**\n * Float value between 0 and 16 indicating the current playback speed of the player.\n * @default 1.0\n */\n playbackRate: number;\n\n /**\n * Boolean value indicating whether the player is currently playing a live stream.\n * > This property is get-only\n */\n isLive: boolean;\n\n /**\n * Indicates the current status of the player.\n * > This property is get-only\n */\n status: VideoPlayerStatus;\n\n /**\n * Boolean value determining whether the player should show the now playing notification.\n */\n showNowPlayingNotification: boolean;\n\n /**\n * Determines whether the player should continue playing after the app enters the background.\n * @default false\n * @platform ios\n * @platform android\n */\n staysActiveInBackground: boolean;\n\n /**\n * Initializes a new video player instance with the given source.\n * @hidden\n */\n constructor(source: VideoSource);\n\n /**\n * Resumes the player.\n */\n play(): void;\n\n /**\n * Pauses the player.\n */\n pause(): void;\n\n /**\n * Replaces the current source with a new one.\n */\n replace(source: VideoSource): void;\n\n /**\n * Seeks the playback by the given number of seconds.\n */\n seekBy(seconds: number): void;\n\n /**\n * Seeks the playback to the beginning.\n */\n replay(): void;\n}\n\n/**\n * Handlers for events which can be emitted by the player.\n */\nexport type VideoPlayerEvents = {\n /**\n * Handler for an event emitted when the status of the player changes.\n */\n statusChange(\n newStatus: VideoPlayerStatus,\n oldStatus: VideoPlayerStatus,\n error: PlayerError\n ): void;\n /**\n * Handler for an event emitted when the player starts or stops playback.\n */\n playingChange(newIsPlaying: boolean, oldIsPlaying: boolean): void;\n /**\n * Handler for an event emitted when the `playbackRate` property of the player changes.\n */\n playbackRateChange(newPlaybackRate: number, oldPlaybackRate: number): void;\n /**\n * Handler for an event emitted when the `volume` property of the player changes.\n */\n volumeChange(newVolume: VolumeEvent, oldVolume: VolumeEvent): void;\n /**\n * Handler for an event emitted when the player plays to the end of the current source.\n */\n playToEnd(): void;\n /**\n * Handler for an event emitted when the current media source of the player changes.\n */\n sourceChange(newSource: VideoSource, previousSource: VideoSource): void;\n};\n\n/**\n * Describes the current status of the player.\n * - `idle`: The player is not playing or loading any videos.\n * - `loading`: The player is loading video data from the provided source\n * - `readyToPlay`: The player has loaded enough data to start playing or to continue playback.\n * - `error`: The player has encountered an error while loading or playing the video.\n */\nexport type VideoPlayerStatus = 'idle' | 'loading' | 'readyToPlay' | 'error';\n\nexport type VideoSource =\n | string\n | {\n /**\n * The URI of the video.\n */\n uri: string;\n /**\n * Specifies the DRM options which will be used by the player while loading the video.\n */\n drm?: DRMOptions;\n /**\n * Specifies information which will be displayed in the now playing notification.\n * When undefined the player will display information contained in the video metadata.\n */\n metadata?: VideoMetadata;\n /**\n * Specifies headers sent with the video request.\n * > For DRM license headers use the `headers` field of [`DRMOptions`](#drmoptions).\n * @platform android\n * @platform ios\n */\n headers?: Record<string, string>;\n }\n | null;\n\n/**\n * Contains information about any errors that the player encountered during the playback\n */\nexport type PlayerError = {\n message: string;\n};\n\n/**\n * Contains information about the current volume and whether the player is muted.\n */\nexport type VolumeEvent = {\n volume: number;\n isMuted: boolean;\n};\n\n/**\n * Contains information that will be displayed in the now playing notification when the video is playing.\n */\nexport type VideoMetadata = {\n /**\n * The title of the video.\n */\n title?: string;\n /**\n * Secondary text that will be displayed under the title.\n */\n artist?: string;\n};\n\n/**\n * Specifies which type of DRM to use. Android supports Widevine, PlayReady and ClearKey, iOS supports FairPlay.\n */\nexport type DRMType = 'clearkey' | 'fairplay' | 'playready' | 'widevine';\n\n/**\n * Specifies DRM options which will be used by the player while loading the video.\n */\nexport type DRMOptions = {\n /**\n * Determines which type of DRM to use.\n */\n type: DRMType;\n\n /**\n * Determines the license server URL.\n */\n licenseServer: string;\n\n /**\n * Determines headers sent to the license server on license requests.\n */\n headers?: { [key: string]: string };\n\n /**\n * Specifies whether the DRM is a multi-key DRM.\n * @platform android\n */\n multiKey?: boolean;\n\n /**\n * Specifies the content ID of the stream.\n * @platform ios\n */\n contentId?: string;\n\n /**\n * Specifies the certificate URL for the FairPlay DRM.\n * @platform ios\n */\n certificateUrl?: string;\n\n /**\n * Specifies the base64 encoded certificate data for the FairPlay DRM.\n * When this property is set, the `certificateUrl` property is ignored.\n * @platform ios\n */\n base64CertificateData?: string;\n};\n"]}
|
|
@@ -19,6 +19,7 @@ export default class VideoPlayerWeb extends globalThis.expo.SharedObject<VideoPl
|
|
|
19
19
|
get muted(): boolean;
|
|
20
20
|
set playbackRate(value: number);
|
|
21
21
|
get playbackRate(): number;
|
|
22
|
+
get isLive(): boolean;
|
|
22
23
|
set volume(value: number);
|
|
23
24
|
get volume(): number;
|
|
24
25
|
set loop(value: boolean);
|
|
@@ -1 +1 @@
|
|
|
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,qBAAqB,CAAC;AAE7B,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,MAAM,CAAC,OAAO,OAAO,cACnB,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,QAAQ,IAAI,MAAM,CAGrB;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;
|
|
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,qBAAqB,CAAC;AAE7B,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,MAAM,CAAC,OAAO,OAAO,cACnB,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,IAAI,OAAO,CAEpB;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,QAAQ,IAAI,MAAM,CAGrB;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;IAgBlC,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
|
@@ -47,6 +47,9 @@ export default class VideoPlayerWeb extends globalThis.expo.SharedObject {
|
|
|
47
47
|
get playbackRate() {
|
|
48
48
|
return this._playbackRate;
|
|
49
49
|
}
|
|
50
|
+
get isLive() {
|
|
51
|
+
return [...this._mountedVideos][0].duration === Infinity;
|
|
52
|
+
}
|
|
50
53
|
set volume(value) {
|
|
51
54
|
this._mountedVideos.forEach((video) => {
|
|
52
55
|
video.volume = value;
|
|
@@ -148,6 +151,7 @@ export default class VideoPlayerWeb extends globalThis.expo.SharedObject {
|
|
|
148
151
|
}
|
|
149
152
|
else {
|
|
150
153
|
video.removeAttribute('src');
|
|
154
|
+
video.load();
|
|
151
155
|
}
|
|
152
156
|
});
|
|
153
157
|
this.playing = true;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VideoPlayer.web.js","sourceRoot":"","sources":["../src/VideoPlayer.web.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAShC,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,OAAO,CAAC,GAAG,EAAE;QAClB,MAAM,MAAM,GAAG,IAAI,cAAc,CAAC,YAAY,CAAC,CAAC;QAChD,KAAK,EAAE,CAAC,MAAM,CAAC,CAAC;QAChB,OAAO,MAAM,CAAC;IAChB,CAAC,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;AAC/B,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,MAAmB;IAC9C,IAAI,OAAO,MAAM,IAAI,QAAQ,EAAE;QAC7B,OAAO,MAAM,CAAC;KACf;IACD,OAAO,MAAM,EAAE,GAAG,IAAI,IAAI,CAAC;AAC7B,CAAC;AAED,MAAM,CAAC,OAAO,OAAO,cACnB,SAAQ,UAAU,CAAC,IAAI,CAAC,YAA+B;IAGvD,YAAY,MAAmB;QAC7B,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,GAAG,GAAG,MAAM,CAAC;IACpB,CAAC;IAED,GAAG,GAAgB,IAAI,CAAC;IACxB,cAAc,GAA0B,IAAI,GAAG,EAAE,CAAC;IAClD,WAAW,GAAqC,IAAI,GAAG,EAAE,CAAC;IAC1D,OAAO,GAAY,KAAK,CAAC;IACzB,MAAM,GAAY,KAAK,CAAC;IACxB,OAAO,GAAW,CAAC,CAAC;IACpB,KAAK,GAAY,KAAK,CAAC;IACvB,aAAa,GAAW,GAAG,CAAC;IAC5B,eAAe,GAAY,IAAI,CAAC;IAChC,OAAO,GAAsB,MAAM,CAAC;IACpC,uBAAuB,GAAY,KAAK,CAAC,CAAC,sDAAsD;IAChG,0BAA0B,GAAY,KAAK,CAAC,CAAC,sDAAsD;IAEnG,IAAI,KAAK,CAAC,KAAc;QACtB,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;YACpC,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC;QACtB,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;IACtB,CAAC;IAED,IAAI,KAAK;QACP,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAED,IAAI,YAAY,CAAC,KAAa;QAC5B,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;YACpC,KAAK,CAAC,YAAY,GAAG,KAAK,CAAC;QAC7B,CAAC,CAAC,CAAC;IACL,CAAC;IAED,IAAI,YAAY;QACd,OAAO,IAAI,CAAC,aAAa,CAAC;IAC5B,CAAC;IAED,IAAI,MAAM,CAAC,KAAa;QACtB,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;YACpC,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC;QACvB,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;IACvB,CAAC;IAED,IAAI,MAAM;QACR,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;YACpC,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC;QAC9B,CAAC,CAAC,CAAC;QACH,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IAED,IAAI,IAAI,CAAC,KAAc;QACrB,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;YACpC,KAAK,CAAC,IAAI,GAAG,KAAK,CAAC;QACrB,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACrB,CAAC;IAED,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;IAED,IAAI,WAAW;QACb,mFAAmF;QACnF,OAAO,CAAC,GAAG,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC;IACjD,CAAC;IAED,IAAI,WAAW,CAAC,KAAa;QAC3B,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;YACpC,KAAK,CAAC,WAAW,GAAG,KAAK,CAAC;QAC5B,CAAC,CAAC,CAAC;IACL,CAAC;IAED,IAAI,QAAQ;QACV,0FAA0F;QAC1F,OAAO,CAAC,GAAG,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;IAC9C,CAAC;IAED,IAAI,cAAc;QAChB,OAAO,IAAI,CAAC,eAAe,CAAC;IAC9B,CAAC;IAED,IAAI,cAAc,CAAC,KAAc;QAC/B,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;YACpC,KAAK,CAAC,cAAc,GAAG,KAAK,CAAC;QAC/B,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,eAAe,GAAG,KAAK,CAAC;IAC/B,CAAC;IAED,IAAI,MAAM;QACR,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IAED,cAAc,CAAC,KAAuB;QACpC,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAC/B,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QAC1B,IAAI,CAAC,0BAA0B,CAAC,KAAK,CAAC,CAAC;IACzC,CAAC;IAED,gBAAgB,CAAC,KAAuB;QACtC,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACpC,CAAC;IAED,cAAc,CACZ,YAA0B,EAC1B,YAAsB,EACtB,eAA4C;QAE5C,IAAI,CAAC,YAAY,IAAI,CAAC,YAAY;YAAE,OAAO;QAE3C,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;QACtC,mGAAmG;QACnG,IAAI,IAAI,CAAC,WAAW,CAAC,IAAI,KAAK,CAAC,EAAE;YAC/B,eAAe,CAAC,OAAO,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC;SACnD;aAAM;YACL,eAAe,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;SACvC;IACH,CAAC;IAED,gBAAgB,CACd,KAAuB,EACvB,YAA0B,EAC1B,eAA4C;QAE5C,MAAM,aAAa,GAAG,CAAC,GAAG,IAAI,CAAC,cAAc,CAAC,CAAC;QAC/C,MAAM,iBAAiB,GAAG,aAAa,CAAC,CAAC,CAAC,CAAC;QAC3C,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;QACzC,eAAe,CAAC,UAAU,EAAE,CAAC;QAE7B,6HAA6H;QAC7H,IAAI,iBAAiB,KAAK,KAAK,IAAI,IAAI,CAAC,WAAW,CAAC,IAAI,GAAG,CAAC,IAAI,YAAY,EAAE;YAC5E,MAAM,kBAAkB,GAAG,CAAC,GAAG,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;YACpD,kBAAkB,CAAC,UAAU,EAAE,CAAC;YAChC,kBAAkB,CAAC,OAAO,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC;SACtD;IACH,CAAC;IAED,IAAI;QACF,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;YACpC,KAAK,CAAC,IAAI,EAAE,CAAC;QACf,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;IACtB,CAAC;IAED,KAAK;QACH,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;YACpC,KAAK,CAAC,KAAK,EAAE,CAAC;QAChB,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;IACvB,CAAC;IAED,OAAO,CAAC,MAAmB;QACzB,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;YACpC,MAAM,GAAG,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;YACjC,KAAK,CAAC,KAAK,EAAE,CAAC;YACd,IAAI,GAAG,EAAE;gBACP,KAAK,CAAC,YAAY,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;gBAC/B,KAAK,CAAC,IAAI,EAAE,CAAC;gBACb,KAAK,CAAC,IAAI,EAAE,CAAC;aACd;iBAAM;gBACL,KAAK,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;aAC9B;QACH,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;IACtB,CAAC;IAED,MAAM,CAAC,OAAe;QACpB,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;YACpC,KAAK,CAAC,WAAW,IAAI,OAAO,CAAC;QAC/B,CAAC,CAAC,CAAC;IACL,CAAC;IAED,MAAM;QACJ,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;YACpC,KAAK,CAAC,WAAW,GAAG,CAAC,CAAC;YACtB,KAAK,CAAC,IAAI,EAAE,CAAC;QACf,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;IACtB,CAAC;IAED,0BAA0B,CAAC,KAAuB;QAChD,MAAM,UAAU,GAAG,CAAC,GAAG,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC;QAC/C,IAAI,CAAC,UAAU;YAAE,OAAO;QAExB,IAAI,UAAU,CAAC,MAAM,EAAE;YACrB,KAAK,CAAC,KAAK,EAAE,CAAC;SACf;aAAM;YACL,KAAK,CAAC,IAAI,EAAE,CAAC;SACd;QACD,KAAK,CAAC,WAAW,GAAG,UAAU,CAAC,WAAW,CAAC;QAC3C,KAAK,CAAC,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC;QACjC,KAAK,CAAC,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC;QAC/B,KAAK,CAAC,YAAY,GAAG,UAAU,CAAC,YAAY,CAAC;IAC/C,CAAC;IAED,aAAa,CAAC,KAAuB;QACnC,KAAK,CAAC,MAAM,GAAG,GAAG,EAAE;YAClB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;YACpB,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,YAAY,EAAE,EAAE;gBAC3C,YAAY,CAAC,IAAI,EAAE,CAAC;YACtB,CAAC,CAAC,CAAC;QACL,CAAC,CAAC;QAEF,KAAK,CAAC,OAAO,GAAG,GAAG,EAAE;YACnB,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;YACrB,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,YAAY,EAAE,EAAE;gBAC3C,YAAY,CAAC,KAAK,EAAE,CAAC;YACvB,CAAC,CAAC,CAAC;QACL,CAAC,CAAC;QAEF,KAAK,CAAC,cAAc,GAAG,GAAG,EAAE;YAC1B,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;YAC3B,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;QAC3B,CAAC,CAAC;QAEF,KAAK,CAAC,SAAS,GAAG,GAAG,EAAE;YACrB,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,YAAY,EAAE,EAAE;gBAC3C,IAAI,YAAY,KAAK,KAAK,IAAI,YAAY,CAAC,WAAW,KAAK,KAAK,CAAC,WAAW;oBAAE,OAAO;gBACrF,YAAY,CAAC,WAAW,GAAG,KAAK,CAAC,WAAW,CAAC;YAC/C,CAAC,CAAC,CAAC;QACL,CAAC,CAAC;QAEF,KAAK,CAAC,QAAQ,GAAG,GAAG,EAAE;YACpB,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,YAAY,EAAE,EAAE;gBAC3C,IAAI,YAAY,KAAK,KAAK,IAAI,YAAY,CAAC,WAAW,KAAK,KAAK,CAAC,WAAW;oBAAE,OAAO;gBACrF,YAAY,CAAC,WAAW,GAAG,KAAK,CAAC,WAAW,CAAC;YAC/C,CAAC,CAAC,CAAC;QACL,CAAC,CAAC;QAEF,KAAK,CAAC,YAAY,GAAG,GAAG,EAAE;YACxB,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,YAAY,EAAE,EAAE;gBAC3C,IAAI,YAAY,KAAK,KAAK,IAAI,YAAY,CAAC,YAAY,KAAK,KAAK,CAAC,YAAY;oBAAE,OAAO;gBACvF,IAAI,CAAC,aAAa,GAAG,KAAK,CAAC,YAAY,CAAC;gBACxC,YAAY,CAAC,YAAY,GAAG,KAAK,CAAC,YAAY,CAAC;YACjD,CAAC,CAAC,CAAC;QACL,CAAC,CAAC;QAEF,KAAK,CAAC,OAAO,GAAG,GAAG,EAAE;YACnB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACzB,CAAC,CAAC;QAEF,KAAK,CAAC,YAAY,GAAG,GAAG,EAAE;YACxB,IAAI,CAAC,OAAO,GAAG,aAAa,CAAC;YAE7B,IAAI,IAAI,CAAC,OAAO,IAAI,KAAK,CAAC,MAAM,EAAE;gBAChC,KAAK,CAAC,IAAI,EAAE,CAAC;aACd;QACH,CAAC,CAAC;QAEF,KAAK,CAAC,SAAS,GAAG,GAAG,EAAE;YACrB,IAAI,CAAC,OAAO,GAAG,SAAS,CAAC;QAC3B,CAAC,CAAC;IACJ,CAAC;CACF","sourcesContent":["import { useMemo } from 'react';\n\nimport type {\n VideoPlayer,\n VideoPlayerEvents,\n VideoPlayerStatus,\n VideoSource,\n} from './VideoPlayer.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 default 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 duration(): number {\n // All videos should have the same duration, so we return the duration of the first video.\n return [...this._mountedVideos][0].duration;\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 './VideoPlayer.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 default 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 get isLive(): boolean {\n return [...this._mountedVideos][0].duration === Infinity;\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 duration(): number {\n // All videos should have the same duration, so we return the duration of the first video.\n return [...this._mountedVideos][0].duration;\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 video.load();\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.web.js
CHANGED
|
@@ -61,7 +61,7 @@ export const VideoView = forwardRef((props, ref) => {
|
|
|
61
61
|
}
|
|
62
62
|
};
|
|
63
63
|
}, [props.player]);
|
|
64
|
-
return (<video controls={props.nativeControls} controlsList={props.allowsFullscreen ? undefined : 'nofullscreen'} crossOrigin="anonymous" style={{
|
|
64
|
+
return (<video controls={props.nativeControls ?? true} controlsList={props.allowsFullscreen ? undefined : 'nofullscreen'} crossOrigin="anonymous" style={{
|
|
65
65
|
...mapStyles(props.style),
|
|
66
66
|
objectFit: props.contentFit,
|
|
67
67
|
}} ref={(newRef) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VideoView.web.js","sourceRoot":"","sources":["../src/VideoView.web.tsx"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"VideoView.web.js","sourceRoot":"","sources":["../src/VideoView.web.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, { useEffect, useRef, forwardRef, useImperativeHandle } from 'react';\nimport { StyleSheet } from 'react-native';\n\nimport VideoPlayer, { getSourceUri } from './VideoPlayer.web';\nimport type { VideoViewProps } from './VideoView.types';\n\nfunction createAudioContext(): AudioContext | null {\n return typeof window !== 'undefined' ? new window.AudioContext() : null;\n}\n\nfunction createZeroGainNode(audioContext: AudioContext | null): GainNode | null {\n const zeroGainNode = audioContext?.createGain() ?? null;\n\n if (audioContext && zeroGainNode) {\n zeroGainNode.gain.value = 0;\n zeroGainNode.connect(audioContext.destination);\n }\n return zeroGainNode;\n}\n\nfunction mapStyles(style: VideoViewProps['style']): React.CSSProperties {\n const flattenedStyles = StyleSheet.flatten(style);\n // Looking through react-native-web source code they also just pass styles directly without further conversions, so it's just a cast.\n return flattenedStyles as React.CSSProperties;\n}\n\nexport const VideoView = forwardRef((props: { player?: VideoPlayer } & VideoViewProps, ref) => {\n const videoRef = useRef<null | HTMLVideoElement>(null);\n const mediaNodeRef = useRef<null | MediaElementAudioSourceNode>(null);\n\n /**\n * Audio context is used to mute all but one video when multiple video views are playing from one player simultaneously.\n * Using audio context nodes allows muting videos without displaying the mute icon in the video player.\n * We have to keep the context that called createMediaElementSource(videoRef), as the method can't be called\n * for the second time with another context and there is no way to unbind the video and audio context afterward.\n */\n const audioContextRef = useRef<null | AudioContext>(null);\n const zeroGainNodeRef = useRef<null | GainNode>(null);\n\n useImperativeHandle(ref, () => ({\n enterFullscreen: () => {\n if (!props.allowsFullscreen) {\n return;\n }\n videoRef.current?.requestFullscreen();\n },\n exitFullscreen: () => {\n document.exitFullscreen();\n },\n }));\n\n useEffect(() => {\n const audioContext = audioContextRef.current;\n const zeroGainNode = zeroGainNodeRef.current;\n const mediaNode = mediaNodeRef.current;\n\n if (videoRef.current) {\n props.player?.mountVideoView(videoRef.current);\n }\n\n if (audioContext && zeroGainNode && mediaNode) {\n props.player.mountAudioNode(audioContext, zeroGainNode, mediaNode);\n } else {\n console.warn(\n \"Couldn't mount audio node, this might affect the audio playback when using multiple video views with the same player.\"\n );\n }\n\n return () => {\n if (videoRef.current) {\n props.player?.unmountVideoView(videoRef.current);\n }\n if (videoRef.current && audioContext && mediaNode) {\n props.player?.unmountAudioNode(videoRef.current, audioContext, mediaNode);\n }\n };\n }, [props.player]);\n\n return (\n <video\n controls={props.nativeControls ?? true}\n controlsList={props.allowsFullscreen ? undefined : 'nofullscreen'}\n crossOrigin=\"anonymous\"\n style={{\n ...mapStyles(props.style),\n objectFit: props.contentFit,\n }}\n ref={(newRef) => {\n // This is called with a null value before `player.unmountVideoView` is called,\n // we can't assign null to videoRef if we want to unmount it from the player.\n if (newRef && !newRef.isEqualNode(videoRef.current)) {\n videoRef.current = newRef;\n const audioContext = createAudioContext();\n audioContextRef.current = audioContext;\n zeroGainNodeRef.current = createZeroGainNode(audioContextRef.current);\n mediaNodeRef.current = audioContext\n ? audioContext.createMediaElementSource(newRef)\n : null;\n }\n }}\n src={getSourceUri(props.player?.src) ?? ''}\n />\n );\n});\n\nexport default VideoView;\n"]}
|
|
@@ -95,10 +95,17 @@ internal class ContentKeyDelegate: NSObject, AVContentKeySessionDelegate {
|
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
private func requestApplicationCertificate(keyRequest: AVContentKeyRequest) throws -> Data {
|
|
98
|
+
if let certificateData = videoSource?.drm?.base64CertificateData {
|
|
99
|
+
return try requestCertificateFrom(base64String: certificateData)
|
|
100
|
+
}
|
|
101
|
+
|
|
98
102
|
guard let url = videoSource?.drm?.certificateUrl else {
|
|
99
|
-
throw DRMLoadException("The certificate uri
|
|
103
|
+
throw DRMLoadException("The certificate uri and data are null")
|
|
100
104
|
}
|
|
105
|
+
return try requestCertificateFrom(url: url)
|
|
106
|
+
}
|
|
101
107
|
|
|
108
|
+
private func requestCertificateFrom(url: URL) throws -> Data {
|
|
102
109
|
let urlRequest = URLRequest(url: url)
|
|
103
110
|
let (data, response, error) = URLSession.shared.synchronousDataTask(with: urlRequest)
|
|
104
111
|
|
|
@@ -123,6 +130,13 @@ internal class ContentKeyDelegate: NSObject, AVContentKeySessionDelegate {
|
|
|
123
130
|
return data
|
|
124
131
|
}
|
|
125
132
|
|
|
133
|
+
private func requestCertificateFrom(base64String: String) throws -> Data {
|
|
134
|
+
guard let certificateData = Data(base64Encoded: base64String, options: .ignoreUnknownCharacters) else {
|
|
135
|
+
throw DRMLoadException("Failed to load the application certificate from the provided base64 string")
|
|
136
|
+
}
|
|
137
|
+
return certificateData
|
|
138
|
+
}
|
|
139
|
+
|
|
126
140
|
private func requestContentKeyFromKeySecurityModule(spcData: Data, assetID: String, keyRequest: AVContentKeyRequest) throws -> Data {
|
|
127
141
|
let ckcData: Data? = nil
|
|
128
142
|
|
|
@@ -13,6 +13,7 @@ class NowPlayingManager: VideoPlayerObserverDelegate {
|
|
|
13
13
|
static var shared = NowPlayingManager()
|
|
14
14
|
|
|
15
15
|
private let skipTimeInterval = 10.0
|
|
16
|
+
private let fetchMetadataQueue = DispatchQueue(label: "com.expo.fetchMetadataQueue")
|
|
16
17
|
private var timeObserver: Any?
|
|
17
18
|
private weak var mostRecentInteractionPlayer: AVPlayer?
|
|
18
19
|
private var players = NSHashTable<VideoPlayer>.weakObjects()
|
|
@@ -138,40 +139,53 @@ class NowPlayingManager: VideoPlayerObserverDelegate {
|
|
|
138
139
|
}
|
|
139
140
|
|
|
140
141
|
private func updateNowPlayingInfo() {
|
|
141
|
-
guard let player = mostRecentInteractionPlayer, let currentItem =
|
|
142
|
+
guard let player = mostRecentInteractionPlayer, let currentItem = player.currentItem else {
|
|
142
143
|
return
|
|
143
144
|
}
|
|
144
145
|
let videoPlayerItem = currentItem as? VideoPlayerItem
|
|
145
146
|
|
|
146
|
-
// Metadata
|
|
147
|
+
// Metadata explicitly specified by the user
|
|
147
148
|
let userMetadata = videoPlayerItem?.videoSource.metadata
|
|
148
149
|
|
|
149
|
-
|
|
150
|
-
|
|
150
|
+
Task {
|
|
151
|
+
let assetMetadata = await try loadMetadata(for: currentItem)
|
|
151
152
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
153
|
+
let title = assetMetadata.first(where: {
|
|
154
|
+
$0.commonKey == .commonKeyTitle
|
|
155
|
+
})
|
|
155
156
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
157
|
+
let artist = assetMetadata.first(where: {
|
|
158
|
+
$0.commonKey == .commonKeyArtist
|
|
159
|
+
})
|
|
159
160
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
161
|
+
let artwork = assetMetadata.first(where: {
|
|
162
|
+
$0.commonKey == .commonKeyArtwork
|
|
163
|
+
})
|
|
163
164
|
|
|
164
|
-
|
|
165
|
+
var nowPlayingInfo = MPNowPlayingInfoCenter.default().nowPlayingInfo ?? [:]
|
|
165
166
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
167
|
+
nowPlayingInfo[MPMediaItemPropertyTitle] = userMetadata?.title ?? title
|
|
168
|
+
nowPlayingInfo[MPMediaItemPropertyArtist] = userMetadata?.artist ?? artist
|
|
169
|
+
nowPlayingInfo[MPMediaItemPropertyPlaybackDuration] = currentItem.duration.seconds
|
|
170
|
+
nowPlayingInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = currentItem.currentTime().seconds
|
|
171
|
+
nowPlayingInfo[MPNowPlayingInfoPropertyPlaybackRate] = await player.rate
|
|
172
|
+
nowPlayingInfo[MPNowPlayingInfoPropertyMediaType] = MPNowPlayingInfoMediaType.video.rawValue // Using MPNowPlayingInfoMediaType.video causes a crash
|
|
173
|
+
nowPlayingInfo[MPMediaItemPropertyArtwork] = artwork
|
|
173
174
|
|
|
174
|
-
|
|
175
|
+
MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlayingInfo
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
private func loadMetadata(for mediaItem: AVPlayerItem) async throws -> [AVMetadataItem] {
|
|
180
|
+
if #available(iOS 15.0, *) {
|
|
181
|
+
return try await mediaItem.asset.loadMetadata(for: .iTunesMetadata)
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return await withCheckedContinuation { continuation in
|
|
185
|
+
fetchMetadataQueue.async {
|
|
186
|
+
continuation.resume(returning: mediaItem.asset.metadata)
|
|
187
|
+
}
|
|
188
|
+
}
|
|
175
189
|
}
|
|
176
190
|
|
|
177
191
|
// Updates nowPlaying information that changes dynamically during playback e.g. progress
|
|
@@ -13,12 +13,12 @@ internal struct DRMOptions: Record {
|
|
|
13
13
|
@Field
|
|
14
14
|
var headers: [String: Any]?
|
|
15
15
|
|
|
16
|
-
@Field
|
|
17
|
-
var base64Certificate: Bool = false
|
|
18
|
-
|
|
19
16
|
@Field
|
|
20
17
|
var contentId: String?
|
|
21
18
|
|
|
22
19
|
@Field
|
|
23
20
|
var certificateUrl: URL?
|
|
21
|
+
|
|
22
|
+
@Field
|
|
23
|
+
var base64CertificateData: String?
|
|
24
24
|
}
|
package/ios/VideoModule.swift
CHANGED
|
@@ -77,7 +77,7 @@ public final class VideoModule: Module {
|
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
Class(VideoPlayer.self) {
|
|
80
|
-
Constructor { (source: VideoSource) -> VideoPlayer in
|
|
80
|
+
Constructor { (source: VideoSource?) -> VideoPlayer in
|
|
81
81
|
let player = AVPlayer()
|
|
82
82
|
let videoPlayer = VideoPlayer(player)
|
|
83
83
|
|
|
@@ -136,6 +136,10 @@ public final class VideoModule: Module {
|
|
|
136
136
|
player.playbackRate = playbackRate
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
+
Property("isLive") { player -> Bool in
|
|
140
|
+
return player.pointer.currentItem?.duration.isIndefinite ?? false
|
|
141
|
+
}
|
|
142
|
+
|
|
139
143
|
Property("preservesPitch") { player -> Bool in
|
|
140
144
|
return player.preservesPitch
|
|
141
145
|
}
|
|
@@ -150,8 +154,8 @@ public final class VideoModule: Module {
|
|
|
150
154
|
player.showNowPlayingNotification = showNowPlayingNotification
|
|
151
155
|
}
|
|
152
156
|
|
|
153
|
-
Property("status") { player
|
|
154
|
-
return player.status
|
|
157
|
+
Property("status") { player in
|
|
158
|
+
return player.status.rawValue
|
|
155
159
|
}
|
|
156
160
|
|
|
157
161
|
Property("volume") { player -> Float in
|
|
@@ -169,7 +173,11 @@ public final class VideoModule: Module {
|
|
|
169
173
|
player.pointer.pause()
|
|
170
174
|
}
|
|
171
175
|
|
|
172
|
-
Function("replace") { (player, source: Either<String, VideoSource
|
|
176
|
+
Function("replace") { (player, source: Either<String, VideoSource>?) in
|
|
177
|
+
guard let source else {
|
|
178
|
+
try player.replaceCurrentItem(with: nil)
|
|
179
|
+
return
|
|
180
|
+
}
|
|
173
181
|
var videoSource: VideoSource?
|
|
174
182
|
|
|
175
183
|
if source.is(String.self), let url: String = source.get() {
|
package/ios/VideoPlayer.swift
CHANGED
|
@@ -83,9 +83,9 @@ internal final class VideoPlayer: SharedRef<AVPlayer>, Hashable, VideoPlayerObse
|
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
deinit {
|
|
86
|
+
observer.cleanup()
|
|
86
87
|
NowPlayingManager.shared.unregisterPlayer(self)
|
|
87
88
|
VideoManager.shared.unregister(videoPlayer: self)
|
|
88
|
-
observer.unregisterDelegate(delegate: self)
|
|
89
89
|
pointer.replaceCurrentItem(with: nil)
|
|
90
90
|
}
|
|
91
91
|
|
|
@@ -98,7 +98,7 @@ internal final class VideoPlayer: SharedRef<AVPlayer>, Hashable, VideoPlayerObse
|
|
|
98
98
|
return
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
-
let asset = AVURLAsset(url: url)
|
|
101
|
+
let asset = AVURLAsset(url: url, options: ["AVURLAssetHTTPHeaderFieldsKey": videoSource.headers])
|
|
102
102
|
let playerItem = VideoPlayerItem(asset: asset, videoSource: videoSource)
|
|
103
103
|
|
|
104
104
|
if let drm = videoSource.drm {
|
|
@@ -58,14 +58,13 @@ final class WeakPlayerObserverDelegate: Hashable {
|
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
class VideoPlayerObserver {
|
|
61
|
-
|
|
61
|
+
weak var player: AVPlayer?
|
|
62
62
|
var delegates = Set<WeakPlayerObserverDelegate>()
|
|
63
|
-
weak var delegate: VideoPlayerObserverDelegate?
|
|
64
63
|
private var currentItem: VideoPlayerItem?
|
|
65
64
|
|
|
66
65
|
private var isPlaying: Bool = false {
|
|
67
66
|
didSet {
|
|
68
|
-
if oldValue != isPlaying {
|
|
67
|
+
if let player, oldValue != isPlaying {
|
|
69
68
|
delegates.forEach { delegate in
|
|
70
69
|
delegate.value?.onIsPlayingChanged(player: player, oldIsPlaying: oldValue, newIsPlaying: isPlaying)
|
|
71
70
|
}
|
|
@@ -75,7 +74,7 @@ class VideoPlayerObserver {
|
|
|
75
74
|
private var error: Exception?
|
|
76
75
|
private var status: PlayerStatus = .idle {
|
|
77
76
|
didSet {
|
|
78
|
-
if oldValue != status {
|
|
77
|
+
if let player, oldValue != status {
|
|
79
78
|
delegates.forEach { delegate in
|
|
80
79
|
delegate.value?.onStatusChanged(player: player, oldStatus: oldValue, newStatus: status, error: error)
|
|
81
80
|
}
|
|
@@ -104,8 +103,7 @@ class VideoPlayerObserver {
|
|
|
104
103
|
}
|
|
105
104
|
|
|
106
105
|
deinit {
|
|
107
|
-
|
|
108
|
-
invalidateCurrentPlayerItemObservers()
|
|
106
|
+
cleanup()
|
|
109
107
|
}
|
|
110
108
|
|
|
111
109
|
func registerDelegate(delegate: VideoPlayerObserverDelegate) {
|
|
@@ -117,13 +115,34 @@ class VideoPlayerObserver {
|
|
|
117
115
|
delegates.remove(WeakPlayerObserverDelegate(value: delegate))
|
|
118
116
|
}
|
|
119
117
|
|
|
118
|
+
func cleanup() {
|
|
119
|
+
delegates.removeAll()
|
|
120
|
+
invalidatePlayerObservers()
|
|
121
|
+
invalidateCurrentPlayerItemObservers()
|
|
122
|
+
}
|
|
123
|
+
|
|
120
124
|
private func initializePlayerObservers() {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
125
|
+
guard let player else {
|
|
126
|
+
return
|
|
127
|
+
}
|
|
128
|
+
playerRateObserver = player.observe(\.rate, options: [.initial, .new, .old]) { [weak self] player, change in
|
|
129
|
+
self?.onPlayerRateChanged(player, change)
|
|
130
|
+
}
|
|
131
|
+
playerStatusObserver = player.observe(\.status, options: [.initial, .new, .old]) { [weak self] player, change in
|
|
132
|
+
self?.onPlayerStatusChanged(player, change)
|
|
133
|
+
}
|
|
134
|
+
playerTimeControlStatusObserver = player.observe(\.timeControlStatus, options: [.new, .old]) { [weak self] player, change in
|
|
135
|
+
self?.onTimeControlStatusChanged(player, change)
|
|
136
|
+
}
|
|
137
|
+
playerVolumeObserver = player.observe(\.volume, options: [.initial, .new, .old]) { [weak self] player, change in
|
|
138
|
+
self?.onPlayerVolumeChanged(player, change)
|
|
139
|
+
}
|
|
140
|
+
playerIsMutedObserver = player.observe(\.isMuted, options: [.initial, .new, .old]) { [weak self] player, change in
|
|
141
|
+
self?.onPlayerIsMutedChanged(player, change)
|
|
142
|
+
}
|
|
143
|
+
playerCurrentItemObserver = player.observe(\.currentItem, options: [.initial, .new]) { [weak self] player, change in
|
|
144
|
+
self?.onPlayerCurrentItemChanged(player, change)
|
|
145
|
+
}
|
|
127
146
|
}
|
|
128
147
|
|
|
129
148
|
private func invalidatePlayerObservers() {
|
|
@@ -136,9 +155,17 @@ class VideoPlayerObserver {
|
|
|
136
155
|
}
|
|
137
156
|
|
|
138
157
|
private func initializeCurrentPlayerItemObservers(player: AVPlayer, playerItem: AVPlayerItem) {
|
|
139
|
-
playbackBufferEmptyObserver = playerItem.observe(\.isPlaybackBufferEmpty,
|
|
140
|
-
|
|
141
|
-
|
|
158
|
+
playbackBufferEmptyObserver = playerItem.observe(\.isPlaybackBufferEmpty) { [weak self] item, change in
|
|
159
|
+
self?.onIsBufferEmptyChanged(item, change)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
playbackLikelyToKeepUpObserver = playerItem.observe(\.isPlaybackLikelyToKeepUp) { [weak self] item, change in
|
|
163
|
+
self?.onPlayerLikelyToKeepUpChanged(item, change)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
playerItemStatusObserver = playerItem.observe(\.status, options: [.initial, .new]) { [weak self] item, change in
|
|
167
|
+
self?.onItemStatusChanged(item, change)
|
|
168
|
+
}
|
|
142
169
|
|
|
143
170
|
playerItemObserver = NotificationCenter.default.addObserver(
|
|
144
171
|
forName: NSNotification.Name.AVPlayerItemDidPlayToEndTime,
|
|
@@ -191,7 +218,7 @@ class VideoPlayerObserver {
|
|
|
191
218
|
}
|
|
192
219
|
|
|
193
220
|
private func onItemStatusChanged(_ playerItem: AVPlayerItem, _ change: NSKeyValueObservedChange<AVPlayerItem.Status>) {
|
|
194
|
-
if player
|
|
221
|
+
if player?.status != .failed {
|
|
195
222
|
error = nil
|
|
196
223
|
}
|
|
197
224
|
|
|
@@ -210,7 +237,9 @@ class VideoPlayerObserver {
|
|
|
210
237
|
}
|
|
211
238
|
|
|
212
239
|
delegates.forEach { delegate in
|
|
213
|
-
|
|
240
|
+
if let player {
|
|
241
|
+
delegate.value?.onPlayerItemStatusChanged(player: player, oldStatus: change.oldValue, newStatus: playerItem.status)
|
|
242
|
+
}
|
|
214
243
|
}
|
|
215
244
|
}
|
|
216
245
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "expo-video",
|
|
3
3
|
"title": "Expo Video",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.2.0",
|
|
5
5
|
"description": "A cross-platform, performant video component for React Native and Expo with Web support",
|
|
6
6
|
"main": "build/index.js",
|
|
7
7
|
"types": "build/index.d.ts",
|
|
@@ -36,5 +36,5 @@
|
|
|
36
36
|
"peerDependencies": {
|
|
37
37
|
"expo": "*"
|
|
38
38
|
},
|
|
39
|
-
"gitHead": "
|
|
39
|
+
"gitHead": "0d1a3567cb0fce9c54e1185654be88bd0c7842d4"
|
|
40
40
|
}
|
package/src/VideoPlayer.types.ts
CHANGED
|
@@ -62,6 +62,12 @@ export declare class VideoPlayer extends SharedObject<VideoPlayerEvents> {
|
|
|
62
62
|
*/
|
|
63
63
|
playbackRate: number;
|
|
64
64
|
|
|
65
|
+
/**
|
|
66
|
+
* Boolean value indicating whether the player is currently playing a live stream.
|
|
67
|
+
* > This property is get-only
|
|
68
|
+
*/
|
|
69
|
+
isLive: boolean;
|
|
70
|
+
|
|
65
71
|
/**
|
|
66
72
|
* Indicates the current status of the player.
|
|
67
73
|
* > This property is get-only
|
|
@@ -172,6 +178,13 @@ export type VideoSource =
|
|
|
172
178
|
* When undefined the player will display information contained in the video metadata.
|
|
173
179
|
*/
|
|
174
180
|
metadata?: VideoMetadata;
|
|
181
|
+
/**
|
|
182
|
+
* Specifies headers sent with the video request.
|
|
183
|
+
* > For DRM license headers use the `headers` field of [`DRMOptions`](#drmoptions).
|
|
184
|
+
* @platform android
|
|
185
|
+
* @platform ios
|
|
186
|
+
*/
|
|
187
|
+
headers?: Record<string, string>;
|
|
175
188
|
}
|
|
176
189
|
| null;
|
|
177
190
|
|
|
@@ -245,4 +258,11 @@ export type DRMOptions = {
|
|
|
245
258
|
* @platform ios
|
|
246
259
|
*/
|
|
247
260
|
certificateUrl?: string;
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Specifies the base64 encoded certificate data for the FairPlay DRM.
|
|
264
|
+
* When this property is set, the `certificateUrl` property is ignored.
|
|
265
|
+
* @platform ios
|
|
266
|
+
*/
|
|
267
|
+
base64CertificateData?: string;
|
|
248
268
|
};
|
package/src/VideoPlayer.web.tsx
CHANGED
|
@@ -70,6 +70,10 @@ export default class VideoPlayerWeb
|
|
|
70
70
|
return this._playbackRate;
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
get isLive(): boolean {
|
|
74
|
+
return [...this._mountedVideos][0].duration === Infinity;
|
|
75
|
+
}
|
|
76
|
+
|
|
73
77
|
set volume(value: number) {
|
|
74
78
|
this._mountedVideos.forEach((video) => {
|
|
75
79
|
video.volume = value;
|
|
@@ -194,6 +198,7 @@ export default class VideoPlayerWeb
|
|
|
194
198
|
video.play();
|
|
195
199
|
} else {
|
|
196
200
|
video.removeAttribute('src');
|
|
201
|
+
video.load();
|
|
197
202
|
}
|
|
198
203
|
});
|
|
199
204
|
this.playing = true;
|
package/src/VideoView.web.tsx
CHANGED
|
@@ -78,7 +78,7 @@ export const VideoView = forwardRef((props: { player?: VideoPlayer } & VideoView
|
|
|
78
78
|
|
|
79
79
|
return (
|
|
80
80
|
<video
|
|
81
|
-
controls={props.nativeControls}
|
|
81
|
+
controls={props.nativeControls ?? true}
|
|
82
82
|
controlsList={props.allowsFullscreen ? undefined : 'nofullscreen'}
|
|
83
83
|
crossOrigin="anonymous"
|
|
84
84
|
style={{
|