expo-video 3.0.3 → 3.0.5

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.
Files changed (59) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/android/build.gradle +2 -2
  3. package/android/src/main/java/expo/modules/video/FullscreenPlayerActivity.kt +1 -1
  4. package/android/src/main/res/layout/fullscreen_player_activity.xml +3 -1
  5. package/build/VideoView.types.d.ts +13 -0
  6. package/build/VideoView.types.d.ts.map +1 -1
  7. package/build/VideoView.types.js.map +1 -1
  8. package/build/VideoView.web.d.ts.map +1 -1
  9. package/build/VideoView.web.js +18 -2
  10. package/build/VideoView.web.js.map +1 -1
  11. package/expo-module.config.json +1 -1
  12. package/ios/VideoManager.swift +3 -6
  13. package/ios/VideoPlayer.swift +0 -17
  14. package/ios/VideoPlayerObserver.swift +1 -1
  15. package/local-maven-repo/host/exp/exponent/expo.modules.video/{3.0.3/expo.modules.video-3.0.3-sources.jar → 3.0.5/expo.modules.video-3.0.5-sources.jar} +0 -0
  16. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.5/expo.modules.video-3.0.5-sources.jar.md5 +1 -0
  17. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.5/expo.modules.video-3.0.5-sources.jar.sha1 +1 -0
  18. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.5/expo.modules.video-3.0.5-sources.jar.sha256 +1 -0
  19. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.5/expo.modules.video-3.0.5-sources.jar.sha512 +1 -0
  20. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.5/expo.modules.video-3.0.5.aar +0 -0
  21. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.5/expo.modules.video-3.0.5.aar.md5 +1 -0
  22. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.5/expo.modules.video-3.0.5.aar.sha1 +1 -0
  23. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.5/expo.modules.video-3.0.5.aar.sha256 +1 -0
  24. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.5/expo.modules.video-3.0.5.aar.sha512 +1 -0
  25. package/local-maven-repo/host/exp/exponent/expo.modules.video/{3.0.3/expo.modules.video-3.0.3.module → 3.0.5/expo.modules.video-3.0.5.module} +22 -22
  26. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.5/expo.modules.video-3.0.5.module.md5 +1 -0
  27. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.5/expo.modules.video-3.0.5.module.sha1 +1 -0
  28. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.5/expo.modules.video-3.0.5.module.sha256 +1 -0
  29. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.5/expo.modules.video-3.0.5.module.sha512 +1 -0
  30. package/local-maven-repo/host/exp/exponent/expo.modules.video/{3.0.3/expo.modules.video-3.0.3.pom → 3.0.5/expo.modules.video-3.0.5.pom} +1 -1
  31. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.5/expo.modules.video-3.0.5.pom.md5 +1 -0
  32. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.5/expo.modules.video-3.0.5.pom.sha1 +1 -0
  33. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.5/expo.modules.video-3.0.5.pom.sha256 +1 -0
  34. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.5/expo.modules.video-3.0.5.pom.sha512 +1 -0
  35. package/local-maven-repo/host/exp/exponent/expo.modules.video/maven-metadata.xml +4 -4
  36. package/local-maven-repo/host/exp/exponent/expo.modules.video/maven-metadata.xml.md5 +1 -1
  37. package/local-maven-repo/host/exp/exponent/expo.modules.video/maven-metadata.xml.sha1 +1 -1
  38. package/local-maven-repo/host/exp/exponent/expo.modules.video/maven-metadata.xml.sha256 +1 -1
  39. package/local-maven-repo/host/exp/exponent/expo.modules.video/maven-metadata.xml.sha512 +1 -1
  40. package/package.json +3 -3
  41. package/src/VideoView.types.ts +14 -0
  42. package/src/VideoView.web.tsx +18 -2
  43. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.3/expo.modules.video-3.0.3-sources.jar.md5 +0 -1
  44. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.3/expo.modules.video-3.0.3-sources.jar.sha1 +0 -1
  45. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.3/expo.modules.video-3.0.3-sources.jar.sha256 +0 -1
  46. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.3/expo.modules.video-3.0.3-sources.jar.sha512 +0 -1
  47. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.3/expo.modules.video-3.0.3.aar +0 -0
  48. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.3/expo.modules.video-3.0.3.aar.md5 +0 -1
  49. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.3/expo.modules.video-3.0.3.aar.sha1 +0 -1
  50. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.3/expo.modules.video-3.0.3.aar.sha256 +0 -1
  51. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.3/expo.modules.video-3.0.3.aar.sha512 +0 -1
  52. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.3/expo.modules.video-3.0.3.module.md5 +0 -1
  53. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.3/expo.modules.video-3.0.3.module.sha1 +0 -1
  54. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.3/expo.modules.video-3.0.3.module.sha256 +0 -1
  55. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.3/expo.modules.video-3.0.3.module.sha512 +0 -1
  56. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.3/expo.modules.video-3.0.3.pom.md5 +0 -1
  57. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.3/expo.modules.video-3.0.3.pom.sha1 +0 -1
  58. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.3/expo.modules.video-3.0.3.pom.sha256 +0 -1
  59. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.3/expo.modules.video-3.0.3.pom.sha512 +0 -1
package/CHANGELOG.md CHANGED
@@ -10,6 +10,24 @@
10
10
 
11
11
  ### 💡 Others
12
12
 
13
+ ## 3.0.5 — 2025-08-25
14
+
15
+ ### 🛠 Breaking changes
16
+
17
+ - [Android] Always keep the native controls always enabled in fullscreen mode to mimic iOS. ([#39015](https://github.com/expo/expo/pull/39015) by [@behenate](https://github.com/behenate))
18
+
19
+ ## 3.0.4 — 2025-08-21
20
+
21
+ ### 🎉 New features
22
+
23
+ - [Web] Add `useAudioNodePlayback` prop. ([#39039](https://github.com/expo/expo/pull/39039) by [@behenate](https://github.com/behenate))
24
+
25
+ ### 🐛 Bug fixes
26
+
27
+ - [iOS] Fix `sourceLoad` event not being emitted. ([#39023](https://github.com/expo/expo/pull/39023) by [@behenate](https://github.com/behenate))
28
+ - [Web] Fix audio not playing due to conflicting CORS and AudioNode settings. ([#39039](https://github.com/expo/expo/pull/39039) by [@behenate](https://github.com/behenate))
29
+ - [iOS] Background mode playback fix. ([#33706](https://github.com/expo/expo/pull/33706) by [@hromovp](https://github.com/hromovp))
30
+
13
31
  ## 3.0.3 — 2025-08-21
14
32
 
15
33
  ### 🎉 New features
@@ -4,13 +4,13 @@ plugins {
4
4
  }
5
5
 
6
6
  group = 'host.exp.exponent'
7
- version = '3.0.3'
7
+ version = '3.0.5'
8
8
 
9
9
  android {
10
10
  namespace "expo.modules.video"
11
11
  defaultConfig {
12
12
  versionCode 1
13
- versionName '3.0.3'
13
+ versionName '3.0.5'
14
14
  }
15
15
  }
16
16
 
@@ -124,7 +124,7 @@ class FullscreenPlayerActivity : Activity() {
124
124
 
125
125
  override fun onResume() {
126
126
  orientationHelper.startOrientationEventListener()
127
- playerView.useController = videoView.useNativeControls
127
+ playerView.useController = true
128
128
  // Reconfigure subtitles when resuming (handles returning from settings)
129
129
  SubtitleUtils.configureSubtitleView(playerView, this)
130
130
  super.onResume()
@@ -1,6 +1,7 @@
1
1
  <?xml version="1.0" encoding="utf-8"?>
2
2
  <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
3
3
  xmlns:tools="http://schemas.android.com/tools"
4
+ xmlns:app="http://schemas.android.com/apk/res-auto"
4
5
  android:id="@+id/enclosing_layout"
5
6
  android:layout_width="match_parent"
6
7
  android:layout_height="match_parent"
@@ -13,6 +14,7 @@
13
14
  android:layout_width="match_parent"
14
15
  android:layout_height="match_parent"
15
16
  android:background="@android:color/black"
16
- android:keepScreenOn="true"/>
17
+ android:keepScreenOn="true"
18
+ app:use_controller="true" />
17
19
 
18
20
  </FrameLayout>
@@ -23,6 +23,8 @@ export interface VideoViewProps extends ViewProps {
23
23
  player: VideoPlayer;
24
24
  /**
25
25
  * Determines whether native controls should be displayed or not.
26
+ *
27
+ * > **Note**: Due to platform limitations, the native controls are always enabled in fullscreen mode.
26
28
  * @default true
27
29
  */
28
30
  nativeControls?: boolean;
@@ -152,6 +154,17 @@ export interface VideoViewProps extends ViewProps {
152
154
  * @default undefined
153
155
  */
154
156
  crossOrigin?: 'anonymous' | 'use-credentials';
157
+ /**
158
+ * Use Audio Nodes for sound playback. When the same player is playing in multiple video views the audio won't increase in volume
159
+ * as the number of players increases.
160
+ *
161
+ * > **Note**: This property is experimental, when enabled it is known to break audio for some sources. Do not change this property at runtime.
162
+ *
163
+ * @experimental
164
+ * @default false
165
+ * @platform web
166
+ */
167
+ useAudioNodePlayback?: boolean;
155
168
  }
156
169
  /**
157
170
  * Describes the orientation of the video in fullscreen mode. Available values are:
@@ -1 +1 @@
1
- {"version":3,"file":"VideoView.types.d.ts","sourceRoot":"","sources":["../src/VideoView.types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEvD;;;;;GAKG;AACH,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG,OAAO,GAAG,MAAM,CAAC;AAE3D;;;;;;;GAOG;AACH,MAAM,MAAM,WAAW,GAAG,aAAa,GAAG,aAAa,CAAC;AAExD,MAAM,WAAW,cAAe,SAAQ,SAAS;IAC/C;;OAEG;IACH,MAAM,EAAE,WAAW,CAAC;IAEpB;;;OAGG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IAEzB;;;;OAIG;IACH,UAAU,CAAC,EAAE,eAAe,CAAC;IAE7B;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAE3B;;OAEG;IACH,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IAEtC;;;;OAIG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IAEzB;;;;;OAKG;IACH,sBAAsB,CAAC,EAAE,OAAO,CAAC;IAEjC;;;;;OAKG;IACH,WAAW,CAAC,EAAE,WAAW,CAAC;IAE1B;;;;OAIG;IACH,eAAe,CAAC,EAAE;QAAE,EAAE,CAAC,EAAE,MAAM,CAAC;QAAC,EAAE,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAE/C;;;;;OAKG;IACH,uBAAuB,CAAC,EAAE,MAAM,IAAI,CAAC;IAErC;;;;;OAKG;IACH,sBAAsB,CAAC,EAAE,MAAM,IAAI,CAAC;IAEpC;;;;;;;OAOG;IACH,sBAAsB,CAAC,EAAE,OAAO,CAAC;IAEjC;;;OAGG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB;;;;;;;;;;OAUG;IACH,mCAAmC,CAAC,EAAE,OAAO,CAAC;IAE9C;;;;;OAKG;IACH,wBAAwB,CAAC,EAAE,OAAO,CAAC;IAEnC;;OAEG;IACH,iBAAiB,CAAC,EAAE,MAAM,IAAI,CAAC;IAE/B;;OAEG;IACH,gBAAgB,CAAC,EAAE,MAAM,IAAI,CAAC;IAE9B;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,MAAM,IAAI,CAAC;IAEhC;;;;;;OAMG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IAExB;;;;;;;;;OASG;IACH,WAAW,CAAC,EAAE,WAAW,GAAG,iBAAiB,CAAC;CAC/C;AAED;;;;;;;;;GASG;AACH,MAAM,MAAM,qBAAqB,GAC7B,SAAS,GACT,UAAU,GACV,YAAY,GACZ,cAAc,GACd,WAAW,GACX,eAAe,GACf,gBAAgB,CAAC;AAErB;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B;;;;OAIG;IACH,MAAM,EAAE,OAAO,CAAC;IAChB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,qBAAqB,CAAC;IACpC;;;;;;;;;;OAUG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B,CAAC"}
1
+ {"version":3,"file":"VideoView.types.d.ts","sourceRoot":"","sources":["../src/VideoView.types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEvD;;;;;GAKG;AACH,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG,OAAO,GAAG,MAAM,CAAC;AAE3D;;;;;;;GAOG;AACH,MAAM,MAAM,WAAW,GAAG,aAAa,GAAG,aAAa,CAAC;AAExD,MAAM,WAAW,cAAe,SAAQ,SAAS;IAC/C;;OAEG;IACH,MAAM,EAAE,WAAW,CAAC;IAEpB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IAEzB;;;;OAIG;IACH,UAAU,CAAC,EAAE,eAAe,CAAC;IAE7B;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAE3B;;OAEG;IACH,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IAEtC;;;;OAIG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IAEzB;;;;;OAKG;IACH,sBAAsB,CAAC,EAAE,OAAO,CAAC;IAEjC;;;;;OAKG;IACH,WAAW,CAAC,EAAE,WAAW,CAAC;IAE1B;;;;OAIG;IACH,eAAe,CAAC,EAAE;QAAE,EAAE,CAAC,EAAE,MAAM,CAAC;QAAC,EAAE,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAE/C;;;;;OAKG;IACH,uBAAuB,CAAC,EAAE,MAAM,IAAI,CAAC;IAErC;;;;;OAKG;IACH,sBAAsB,CAAC,EAAE,MAAM,IAAI,CAAC;IAEpC;;;;;;;OAOG;IACH,sBAAsB,CAAC,EAAE,OAAO,CAAC;IAEjC;;;OAGG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB;;;;;;;;;;OAUG;IACH,mCAAmC,CAAC,EAAE,OAAO,CAAC;IAE9C;;;;;OAKG;IACH,wBAAwB,CAAC,EAAE,OAAO,CAAC;IAEnC;;OAEG;IACH,iBAAiB,CAAC,EAAE,MAAM,IAAI,CAAC;IAE/B;;OAEG;IACH,gBAAgB,CAAC,EAAE,MAAM,IAAI,CAAC;IAE9B;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,MAAM,IAAI,CAAC;IAEhC;;;;;;OAMG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IAExB;;;;;;;;;OASG;IACH,WAAW,CAAC,EAAE,WAAW,GAAG,iBAAiB,CAAC;IAE9C;;;;;;;;;OASG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC;AAED;;;;;;;;;GASG;AACH,MAAM,MAAM,qBAAqB,GAC7B,SAAS,GACT,UAAU,GACV,YAAY,GACZ,cAAc,GACd,WAAW,GACX,eAAe,GACf,gBAAgB,CAAC;AAErB;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B;;;;OAIG;IACH,MAAM,EAAE,OAAO,CAAC;IAChB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,qBAAqB,CAAC;IACpC;;;;;;;;;;OAUG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"VideoView.types.js","sourceRoot":"","sources":["../src/VideoView.types.ts"],"names":[],"mappings":"","sourcesContent":["import { ViewProps } from 'react-native';\n\nimport type { VideoPlayer } from './VideoPlayer.types';\n\n/**\n * Describes how a video should be scaled to fit in a container.\n * - `contain`: The video maintains its aspect ratio and fits inside the container, with possible letterboxing/pillarboxing.\n * - `cover`: The video maintains its aspect ratio and covers the entire container, potentially cropping some portions.\n * - `fill`: The video stretches/squeezes to completely fill the container, potentially causing distortion.\n */\nexport type VideoContentFit = 'contain' | 'cover' | 'fill';\n\n/**\n * Describes the type of the surface used to render the video.\n * - `surfaceView`: Uses the `SurfaceView` to render the video. This value should be used in the majority of cases. Provides significantly lower power consumption, better performance, and more features.\n * - `textureView`: Uses the `TextureView` to render the video. Should be used in cases where the SurfaceView is not supported or causes issues (for example, overlapping video views).\n *\n * You can learn more about surface types in the official [ExoPlayer documentation](https://developer.android.com/media/media3/ui/playerview#surfacetype).\n * @platform android\n */\nexport type SurfaceType = 'textureView' | 'surfaceView';\n\nexport interface VideoViewProps extends ViewProps {\n /**\n * A video player instance. Use [`useVideoPlayer()`](#usevideoplayersource-setup) hook to create one.\n */\n player: VideoPlayer;\n\n /**\n * Determines whether native controls should be displayed or not.\n * @default true\n */\n nativeControls?: boolean;\n\n /**\n * Describes how the video should be scaled to fit in the container.\n * Options are `'contain'`, `'cover'`, and `'fill'`.\n * @default 'contain'\n */\n contentFit?: VideoContentFit;\n\n /**\n * Determines whether fullscreen mode is allowed or not.\n *\n * > Note: This option has been deprecated in favor of the `fullscreenOptions` prop and will be disabled in the future.\n * @default true\n */\n allowsFullscreen?: boolean;\n\n /**\n * Determines the fullscreen mode options.\n */\n fullscreenOptions?: FullscreenOptions;\n\n /**\n * Determines whether the timecodes should be displayed or not.\n * @default true\n * @platform ios\n */\n showsTimecodes?: boolean;\n\n /**\n * Determines whether the player allows the user to skip media content.\n * @default false\n * @platform android\n * @platform ios\n */\n requiresLinearPlayback?: boolean;\n\n /**\n * Determines the type of the surface used to render the video.\n * > This prop should not be changed at runtime.\n * @default 'surfaceView'\n * @platform android\n */\n surfaceType?: SurfaceType;\n\n /**\n * Determines the position offset of the video inside the container.\n * @default { dx: 0, dy: 0 }\n * @platform ios\n */\n contentPosition?: { dx?: number; dy?: number };\n\n /**\n * A callback to call after the video player enters Picture in Picture (PiP) mode.\n * @platform android\n * @platform ios\n * @platform web\n */\n onPictureInPictureStart?: () => void;\n\n /**\n * A callback to call after the video player exits Picture in Picture (PiP) mode.\n * @platform android\n * @platform ios\n * @platform web\n */\n onPictureInPictureStop?: () => void;\n\n /**\n * Determines whether the player allows Picture in Picture (PiP) mode.\n * > **Note:** The `supportsPictureInPicture` property of the [config plugin](#configuration-in-app-config)\n * > has to be configured for the PiP to work.\n * @platform android\n * @platform ios\n * @platform web\n */\n allowsPictureInPicture?: boolean;\n\n /**\n * Determines whether a video should be played \"inline\", that is, within the element's playback area.\n * @platform web\n */\n playsInline?: boolean;\n\n /**\n * Determines whether the player should start Picture in Picture (PiP) automatically when the app is in the background.\n * > **Note:** Only one player can be in Picture in Picture (PiP) mode at a time.\n *\n * > **Note:** The `supportsPictureInPicture` property of the [config plugin](#configuration-in-app-config)\n * > has to be configured for the PiP to work.\n *\n * @default false\n * @platform android 12+\n * @platform ios\n */\n startsPictureInPictureAutomatically?: boolean;\n\n /**\n * Specifies whether to perform video frame analysis (Live Text in videos).\n * Check official [Apple documentation](https://developer.apple.com/documentation/avkit/avplayerviewcontroller/allowsvideoframeanalysis) for more details.\n * @default true\n * @platform ios 16.0+\n */\n allowsVideoFrameAnalysis?: boolean;\n\n /**\n * A callback to call after the video player enters fullscreen mode.\n */\n onFullscreenEnter?: () => void;\n\n /**\n * A callback to call after the video player exits fullscreen mode.\n */\n onFullscreenExit?: () => void;\n\n /**\n * A callback to call after the mounted `VideoPlayer` has rendered the first frame into the `VideoView`.\n * This event can be used to hide any cover images that conceal the initial loading of the player.\n * > **Note:** This event may also be called during playback when the current video track changes (for example when the player switches video quality).\n */\n onFirstFrameRender?: () => void;\n\n /**\n * Determines whether the player should use the default ExoPlayer shutter that covers the `VideoView` before the first video frame is rendered.\n * Setting this property to `false` makes the Android behavior the same as iOS.\n *\n * @platform android\n * @default false\n */\n useExoShutter?: boolean;\n\n /**\n * Determines the [cross origin policy](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Attributes/crossorigin) used by the underlying native view on web.\n * If `undefined` (default), does not use CORS at all. If set to `'anonymous'`, the video will be loaded with CORS enabled.\n * Note that some videos may not play if CORS is enabled, depending on the CDN settings.\n * If you encounter issues, consider adjusting the `crossOrigin` property.\n *\n *\n * @platform web\n * @default undefined\n */\n crossOrigin?: 'anonymous' | 'use-credentials';\n}\n\n/**\n * Describes the orientation of the video in fullscreen mode. Available values are:\n * - `default`: The video is displayed in any of the available device rotations.\n * - `portrait`: The video is displayed in one of two available portrait orientations and rotates between them.\n * - `portraitUp`: The video is displayed in the portrait orientation - the notch of the phone points upwards.\n * - `portraitDown`: The video is displayed in the portrait orientation - the notch of the phone points downwards.\n * - `landscape`: The video is displayed in one of two available landscape orientations and rotates between them.\n * - `landscapeLeft`: The video is displayed in the left landscape orientation - the notch of the phone is in the left palm of the user.\n * - `landscapeRight`: The video is displayed in the right landscape orientation - the notch of the phone is in the right palm of the user.\n */\nexport type FullscreenOrientation =\n | 'default'\n | 'portrait'\n | 'portraitUp'\n | 'portraitDown'\n | 'landscape'\n | 'landscapeLeft'\n | 'landscapeRight';\n\n/**\n * Describes the options for fullscreen video mode.\n */\nexport type FullscreenOptions = {\n /**\n * Specifies whether the fullscreen mode should be available to the user. When `false`, the fullscreen button will be hidden in the player.\n * Equivalent to the `allowsFullscreen` prop.\n * @default true\n */\n enable: boolean;\n /**\n * Specifies the orientation of the video in fullscreen mode.\n * @default 'default'\n * @platform android\n * @platform ios\n */\n orientation?: FullscreenOrientation;\n /**\n * Specifies whether the app should exit fullscreen mode when the device is rotated to a different orientation than the one specified in the `orientation` prop.\n * For example, if the `orientation` prop is set to `landscape` and the device is rotated to `portrait`, the app will exit fullscreen mode.\n *\n * > This prop will have no effect if the `orientation` prop is set to `default`.\n * > The `VideoView` will never auto-exit fullscreen when the device auto-rotate feature has been disabled in settings.\n *\n * @default false\n * @platform android\n * @platform ios\n */\n autoExitOnRotate?: boolean;\n};\n"]}
1
+ {"version":3,"file":"VideoView.types.js","sourceRoot":"","sources":["../src/VideoView.types.ts"],"names":[],"mappings":"","sourcesContent":["import { ViewProps } from 'react-native';\n\nimport type { VideoPlayer } from './VideoPlayer.types';\n\n/**\n * Describes how a video should be scaled to fit in a container.\n * - `contain`: The video maintains its aspect ratio and fits inside the container, with possible letterboxing/pillarboxing.\n * - `cover`: The video maintains its aspect ratio and covers the entire container, potentially cropping some portions.\n * - `fill`: The video stretches/squeezes to completely fill the container, potentially causing distortion.\n */\nexport type VideoContentFit = 'contain' | 'cover' | 'fill';\n\n/**\n * Describes the type of the surface used to render the video.\n * - `surfaceView`: Uses the `SurfaceView` to render the video. This value should be used in the majority of cases. Provides significantly lower power consumption, better performance, and more features.\n * - `textureView`: Uses the `TextureView` to render the video. Should be used in cases where the SurfaceView is not supported or causes issues (for example, overlapping video views).\n *\n * You can learn more about surface types in the official [ExoPlayer documentation](https://developer.android.com/media/media3/ui/playerview#surfacetype).\n * @platform android\n */\nexport type SurfaceType = 'textureView' | 'surfaceView';\n\nexport interface VideoViewProps extends ViewProps {\n /**\n * A video player instance. Use [`useVideoPlayer()`](#usevideoplayersource-setup) hook to create one.\n */\n player: VideoPlayer;\n\n /**\n * Determines whether native controls should be displayed or not.\n *\n * > **Note**: Due to platform limitations, the native controls are always enabled in fullscreen mode.\n * @default true\n */\n nativeControls?: boolean;\n\n /**\n * Describes how the video should be scaled to fit in the container.\n * Options are `'contain'`, `'cover'`, and `'fill'`.\n * @default 'contain'\n */\n contentFit?: VideoContentFit;\n\n /**\n * Determines whether fullscreen mode is allowed or not.\n *\n * > Note: This option has been deprecated in favor of the `fullscreenOptions` prop and will be disabled in the future.\n * @default true\n */\n allowsFullscreen?: boolean;\n\n /**\n * Determines the fullscreen mode options.\n */\n fullscreenOptions?: FullscreenOptions;\n\n /**\n * Determines whether the timecodes should be displayed or not.\n * @default true\n * @platform ios\n */\n showsTimecodes?: boolean;\n\n /**\n * Determines whether the player allows the user to skip media content.\n * @default false\n * @platform android\n * @platform ios\n */\n requiresLinearPlayback?: boolean;\n\n /**\n * Determines the type of the surface used to render the video.\n * > This prop should not be changed at runtime.\n * @default 'surfaceView'\n * @platform android\n */\n surfaceType?: SurfaceType;\n\n /**\n * Determines the position offset of the video inside the container.\n * @default { dx: 0, dy: 0 }\n * @platform ios\n */\n contentPosition?: { dx?: number; dy?: number };\n\n /**\n * A callback to call after the video player enters Picture in Picture (PiP) mode.\n * @platform android\n * @platform ios\n * @platform web\n */\n onPictureInPictureStart?: () => void;\n\n /**\n * A callback to call after the video player exits Picture in Picture (PiP) mode.\n * @platform android\n * @platform ios\n * @platform web\n */\n onPictureInPictureStop?: () => void;\n\n /**\n * Determines whether the player allows Picture in Picture (PiP) mode.\n * > **Note:** The `supportsPictureInPicture` property of the [config plugin](#configuration-in-app-config)\n * > has to be configured for the PiP to work.\n * @platform android\n * @platform ios\n * @platform web\n */\n allowsPictureInPicture?: boolean;\n\n /**\n * Determines whether a video should be played \"inline\", that is, within the element's playback area.\n * @platform web\n */\n playsInline?: boolean;\n\n /**\n * Determines whether the player should start Picture in Picture (PiP) automatically when the app is in the background.\n * > **Note:** Only one player can be in Picture in Picture (PiP) mode at a time.\n *\n * > **Note:** The `supportsPictureInPicture` property of the [config plugin](#configuration-in-app-config)\n * > has to be configured for the PiP to work.\n *\n * @default false\n * @platform android 12+\n * @platform ios\n */\n startsPictureInPictureAutomatically?: boolean;\n\n /**\n * Specifies whether to perform video frame analysis (Live Text in videos).\n * Check official [Apple documentation](https://developer.apple.com/documentation/avkit/avplayerviewcontroller/allowsvideoframeanalysis) for more details.\n * @default true\n * @platform ios 16.0+\n */\n allowsVideoFrameAnalysis?: boolean;\n\n /**\n * A callback to call after the video player enters fullscreen mode.\n */\n onFullscreenEnter?: () => void;\n\n /**\n * A callback to call after the video player exits fullscreen mode.\n */\n onFullscreenExit?: () => void;\n\n /**\n * A callback to call after the mounted `VideoPlayer` has rendered the first frame into the `VideoView`.\n * This event can be used to hide any cover images that conceal the initial loading of the player.\n * > **Note:** This event may also be called during playback when the current video track changes (for example when the player switches video quality).\n */\n onFirstFrameRender?: () => void;\n\n /**\n * Determines whether the player should use the default ExoPlayer shutter that covers the `VideoView` before the first video frame is rendered.\n * Setting this property to `false` makes the Android behavior the same as iOS.\n *\n * @platform android\n * @default false\n */\n useExoShutter?: boolean;\n\n /**\n * Determines the [cross origin policy](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Attributes/crossorigin) used by the underlying native view on web.\n * If `undefined` (default), does not use CORS at all. If set to `'anonymous'`, the video will be loaded with CORS enabled.\n * Note that some videos may not play if CORS is enabled, depending on the CDN settings.\n * If you encounter issues, consider adjusting the `crossOrigin` property.\n *\n *\n * @platform web\n * @default undefined\n */\n crossOrigin?: 'anonymous' | 'use-credentials';\n\n /**\n * Use Audio Nodes for sound playback. When the same player is playing in multiple video views the audio won't increase in volume\n * as the number of players increases.\n *\n * > **Note**: This property is experimental, when enabled it is known to break audio for some sources. Do not change this property at runtime.\n *\n * @experimental\n * @default false\n * @platform web\n */\n useAudioNodePlayback?: boolean;\n}\n\n/**\n * Describes the orientation of the video in fullscreen mode. Available values are:\n * - `default`: The video is displayed in any of the available device rotations.\n * - `portrait`: The video is displayed in one of two available portrait orientations and rotates between them.\n * - `portraitUp`: The video is displayed in the portrait orientation - the notch of the phone points upwards.\n * - `portraitDown`: The video is displayed in the portrait orientation - the notch of the phone points downwards.\n * - `landscape`: The video is displayed in one of two available landscape orientations and rotates between them.\n * - `landscapeLeft`: The video is displayed in the left landscape orientation - the notch of the phone is in the left palm of the user.\n * - `landscapeRight`: The video is displayed in the right landscape orientation - the notch of the phone is in the right palm of the user.\n */\nexport type FullscreenOrientation =\n | 'default'\n | 'portrait'\n | 'portraitUp'\n | 'portraitDown'\n | 'landscape'\n | 'landscapeLeft'\n | 'landscapeRight';\n\n/**\n * Describes the options for fullscreen video mode.\n */\nexport type FullscreenOptions = {\n /**\n * Specifies whether the fullscreen mode should be available to the user. When `false`, the fullscreen button will be hidden in the player.\n * Equivalent to the `allowsFullscreen` prop.\n * @default true\n */\n enable: boolean;\n /**\n * Specifies the orientation of the video in fullscreen mode.\n * @default 'default'\n * @platform android\n * @platform ios\n */\n orientation?: FullscreenOrientation;\n /**\n * Specifies whether the app should exit fullscreen mode when the device is rotated to a different orientation than the one specified in the `orientation` prop.\n * For example, if the `orientation` prop is set to `landscape` and the device is rotated to `portrait`, the app will exit fullscreen mode.\n *\n * > This prop will have no effect if the `orientation` prop is set to `default`.\n * > The `VideoView` will never auto-exit fullscreen when the device auto-rotate feature has been disabled in settings.\n *\n * @default false\n * @platform android\n * @platform ios\n */\n autoExitOnRotate?: boolean;\n};\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"VideoView.web.d.ts","sourceRoot":"","sources":["../src/VideoView.web.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA6D,MAAM,OAAO,CAAC;AAGlF,OAAO,WAA6B,MAAM,mBAAmB,CAAC;AAC9D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAsBxD,wBAAgB,2BAA2B,IAAI,OAAO,CAErD;AAED,eAAO,MAAM,SAAS;aAAiC,WAAW;kDAqLhE,CAAC;AAEH,eAAe,SAAS,CAAC"}
1
+ {"version":3,"file":"VideoView.web.d.ts","sourceRoot":"","sources":["../src/VideoView.web.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA6D,MAAM,OAAO,CAAC;AAGlF,OAAO,WAA6B,MAAM,mBAAmB,CAAC;AAC9D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAsBxD,wBAAgB,2BAA2B,IAAI,OAAO,CAErD;AAED,eAAO,MAAM,SAAS;aAAiC,WAAW;kDAqMhE,CAAC;AAEH,eAAe,SAAS,CAAC"}
@@ -34,6 +34,15 @@ export const VideoView = forwardRef((props, ref) => {
34
34
  */
35
35
  const audioContextRef = useRef(null);
36
36
  const zeroGainNodeRef = useRef(null);
37
+ useEffect(() => {
38
+ if (props.useAudioNodePlayback) {
39
+ maybeSetupAudioContext();
40
+ attachAudioNodes();
41
+ }
42
+ else {
43
+ detachAudioNodes();
44
+ }
45
+ }, [props.useAudioNodePlayback]);
37
46
  useImperativeHandle(ref, () => ({
38
47
  enterFullscreen: async () => {
39
48
  if (!props.allowsFullscreen) {
@@ -91,6 +100,9 @@ export const VideoView = forwardRef((props, ref) => {
91
100
  // Adds the video view as a candidate for being the audio source for the player (when multiple views play from one
92
101
  // player only one will emit audio).
93
102
  function attachAudioNodes() {
103
+ if (!props.useAudioNodePlayback) {
104
+ return;
105
+ }
94
106
  const audioContext = audioContextRef.current;
95
107
  const zeroGainNode = zeroGainNodeRef.current;
96
108
  const mediaNode = mediaNodeRef.current;
@@ -102,6 +114,9 @@ export const VideoView = forwardRef((props, ref) => {
102
114
  }
103
115
  }
104
116
  function detachAudioNodes() {
117
+ if (!props.useAudioNodePlayback) {
118
+ return;
119
+ }
105
120
  const audioContext = audioContextRef.current;
106
121
  const mediaNode = mediaNodeRef.current;
107
122
  if (audioContext && mediaNode && videoRef.current) {
@@ -111,7 +126,8 @@ export const VideoView = forwardRef((props, ref) => {
111
126
  function maybeSetupAudioContext() {
112
127
  if (!hasToSetupAudioContext.current ||
113
128
  !navigator.userActivation.hasBeenActive ||
114
- !videoRef.current) {
129
+ !videoRef.current ||
130
+ !props.useAudioNodePlayback) {
115
131
  return;
116
132
  }
117
133
  const audioContext = createAudioContext();
@@ -170,7 +186,7 @@ export const VideoView = forwardRef((props, ref) => {
170
186
  // we can't assign null to videoRef if we want to unmount it from the player.
171
187
  if (newRef && !newRef.isEqualNode(videoRef.current)) {
172
188
  videoRef.current = newRef;
173
- hasToSetupAudioContext.current = true;
189
+ hasToSetupAudioContext.current = props.useAudioNodePlayback ?? false;
174
190
  maybeSetupAudioContext();
175
191
  }
176
192
  }} disablePictureInPicture={!props.allowsPictureInPicture} playsInline={props.playsInline} src={getSourceUri(props.player?.src) ?? ''}/>);
@@ -1 +1 @@
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 function isPictureInPictureSupported(): boolean {\n return typeof document === 'object' && typeof document.exitPictureInPicture === 'function';\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 const hasToSetupAudioContext = useRef(false);\n const fullscreenChangeListener = useRef<null | (() => void)>(null);\n const isWaitingForFirstFrame = useRef(false);\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: async () => {\n if (!props.allowsFullscreen) {\n return;\n }\n await videoRef.current?.requestFullscreen();\n },\n exitFullscreen: async () => {\n await document.exitFullscreen();\n },\n startPictureInPicture: async () => {\n await videoRef.current?.requestPictureInPicture();\n },\n stopPictureInPicture: async () => {\n try {\n await document.exitPictureInPicture();\n } catch (e) {\n if (e instanceof DOMException && e.name === 'InvalidStateError') {\n console.warn('The VideoView is not in Picture-in-Picture mode.');\n } else {\n throw e;\n }\n }\n },\n }));\n\n useEffect(() => {\n const onEnter = () => {\n props.onPictureInPictureStart?.();\n };\n const onLeave = () => {\n props.onPictureInPictureStop?.();\n };\n const onLoadStart = () => {\n isWaitingForFirstFrame.current = true;\n };\n const onCanPlay = () => {\n if (isWaitingForFirstFrame.current) {\n props.onFirstFrameRender?.();\n }\n isWaitingForFirstFrame.current = false;\n };\n videoRef.current?.addEventListener('enterpictureinpicture', onEnter);\n videoRef.current?.addEventListener('leavepictureinpicture', onLeave);\n videoRef.current?.addEventListener('loadstart', onLoadStart);\n videoRef.current?.addEventListener('loadeddata', onCanPlay);\n\n return () => {\n videoRef.current?.removeEventListener('enterpictureinpicture', onEnter);\n videoRef.current?.removeEventListener('leavepictureinpicture', onLeave);\n videoRef.current?.removeEventListener('loadstart', onLoadStart);\n videoRef.current?.removeEventListener('loadeddata', onCanPlay);\n };\n }, [videoRef, props.onPictureInPictureStop, props.onPictureInPictureStart]);\n\n // Adds the video view as a candidate for being the audio source for the player (when multiple views play from one\n // player only one will emit audio).\n function attachAudioNodes() {\n const audioContext = audioContextRef.current;\n const zeroGainNode = zeroGainNodeRef.current;\n const mediaNode = mediaNodeRef.current;\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\n function detachAudioNodes() {\n const audioContext = audioContextRef.current;\n const mediaNode = mediaNodeRef.current;\n if (audioContext && mediaNode && videoRef.current) {\n props.player.unmountAudioNode(videoRef.current, audioContext, mediaNode);\n }\n }\n\n function maybeSetupAudioContext() {\n if (\n !hasToSetupAudioContext.current ||\n !navigator.userActivation.hasBeenActive ||\n !videoRef.current\n ) {\n return;\n }\n const audioContext = createAudioContext();\n\n detachAudioNodes();\n audioContextRef.current = audioContext;\n zeroGainNodeRef.current = createZeroGainNode(audioContextRef.current);\n mediaNodeRef.current = audioContext\n ? audioContext.createMediaElementSource(videoRef.current)\n : null;\n attachAudioNodes();\n hasToSetupAudioContext.current = false;\n }\n\n function fullscreenListener() {\n if (document.fullscreenElement === videoRef.current) {\n props.onFullscreenEnter?.();\n } else {\n props.onFullscreenExit?.();\n }\n }\n\n function setupFullscreenListener() {\n fullscreenChangeListener.current = fullscreenListener;\n videoRef.current?.addEventListener('fullscreenchange', fullscreenChangeListener.current);\n }\n\n function cleanupFullscreenListener() {\n if (fullscreenChangeListener.current) {\n videoRef.current?.removeEventListener('fullscreenchange', fullscreenChangeListener.current);\n fullscreenChangeListener.current = null;\n }\n }\n\n useEffect(() => {\n if (videoRef.current) {\n props.player?.mountVideoView(videoRef.current);\n }\n setupFullscreenListener();\n attachAudioNodes();\n\n return () => {\n if (videoRef.current) {\n props.player?.unmountVideoView(videoRef.current);\n }\n cleanupFullscreenListener();\n detachAudioNodes();\n };\n }, [props.player]);\n\n return (\n <video\n controls={props.nativeControls ?? true}\n controlsList={props.allowsFullscreen ? undefined : 'nofullscreen'}\n crossOrigin={props.crossOrigin}\n style={{\n ...mapStyles(props.style),\n objectFit: props.contentFit,\n }}\n onPlay={() => {\n maybeSetupAudioContext();\n }}\n // The player can autoplay when muted, unmuting by a user should create the audio context\n onVolumeChange={() => {\n maybeSetupAudioContext();\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 hasToSetupAudioContext.current = true;\n maybeSetupAudioContext();\n }\n }}\n disablePictureInPicture={!props.allowsPictureInPicture}\n playsInline={props.playsInline}\n src={getSourceUri(props.player?.src) ?? ''}\n />\n );\n});\n\nexport default VideoView;\n"]}
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 function isPictureInPictureSupported(): boolean {\n return typeof document === 'object' && typeof document.exitPictureInPicture === 'function';\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 const hasToSetupAudioContext = useRef(false);\n const fullscreenChangeListener = useRef<null | (() => void)>(null);\n const isWaitingForFirstFrame = useRef(false);\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 useEffect(() => {\n if (props.useAudioNodePlayback) {\n maybeSetupAudioContext();\n attachAudioNodes();\n } else {\n detachAudioNodes();\n }\n }, [props.useAudioNodePlayback]);\n\n useImperativeHandle(ref, () => ({\n enterFullscreen: async () => {\n if (!props.allowsFullscreen) {\n return;\n }\n await videoRef.current?.requestFullscreen();\n },\n exitFullscreen: async () => {\n await document.exitFullscreen();\n },\n startPictureInPicture: async () => {\n await videoRef.current?.requestPictureInPicture();\n },\n stopPictureInPicture: async () => {\n try {\n await document.exitPictureInPicture();\n } catch (e) {\n if (e instanceof DOMException && e.name === 'InvalidStateError') {\n console.warn('The VideoView is not in Picture-in-Picture mode.');\n } else {\n throw e;\n }\n }\n },\n }));\n\n useEffect(() => {\n const onEnter = () => {\n props.onPictureInPictureStart?.();\n };\n const onLeave = () => {\n props.onPictureInPictureStop?.();\n };\n const onLoadStart = () => {\n isWaitingForFirstFrame.current = true;\n };\n const onCanPlay = () => {\n if (isWaitingForFirstFrame.current) {\n props.onFirstFrameRender?.();\n }\n isWaitingForFirstFrame.current = false;\n };\n videoRef.current?.addEventListener('enterpictureinpicture', onEnter);\n videoRef.current?.addEventListener('leavepictureinpicture', onLeave);\n videoRef.current?.addEventListener('loadstart', onLoadStart);\n videoRef.current?.addEventListener('loadeddata', onCanPlay);\n\n return () => {\n videoRef.current?.removeEventListener('enterpictureinpicture', onEnter);\n videoRef.current?.removeEventListener('leavepictureinpicture', onLeave);\n videoRef.current?.removeEventListener('loadstart', onLoadStart);\n videoRef.current?.removeEventListener('loadeddata', onCanPlay);\n };\n }, [videoRef, props.onPictureInPictureStop, props.onPictureInPictureStart]);\n\n // Adds the video view as a candidate for being the audio source for the player (when multiple views play from one\n // player only one will emit audio).\n function attachAudioNodes() {\n if (!props.useAudioNodePlayback) {\n return;\n }\n const audioContext = audioContextRef.current;\n const zeroGainNode = zeroGainNodeRef.current;\n const mediaNode = mediaNodeRef.current;\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\n function detachAudioNodes() {\n if (!props.useAudioNodePlayback) {\n return;\n }\n const audioContext = audioContextRef.current;\n const mediaNode = mediaNodeRef.current;\n if (audioContext && mediaNode && videoRef.current) {\n props.player.unmountAudioNode(videoRef.current, audioContext, mediaNode);\n }\n }\n\n function maybeSetupAudioContext() {\n if (\n !hasToSetupAudioContext.current ||\n !navigator.userActivation.hasBeenActive ||\n !videoRef.current ||\n !props.useAudioNodePlayback\n ) {\n return;\n }\n const audioContext = createAudioContext();\n\n detachAudioNodes();\n audioContextRef.current = audioContext;\n zeroGainNodeRef.current = createZeroGainNode(audioContextRef.current);\n mediaNodeRef.current = audioContext\n ? audioContext.createMediaElementSource(videoRef.current)\n : null;\n attachAudioNodes();\n hasToSetupAudioContext.current = false;\n }\n\n function fullscreenListener() {\n if (document.fullscreenElement === videoRef.current) {\n props.onFullscreenEnter?.();\n } else {\n props.onFullscreenExit?.();\n }\n }\n\n function setupFullscreenListener() {\n fullscreenChangeListener.current = fullscreenListener;\n videoRef.current?.addEventListener('fullscreenchange', fullscreenChangeListener.current);\n }\n\n function cleanupFullscreenListener() {\n if (fullscreenChangeListener.current) {\n videoRef.current?.removeEventListener('fullscreenchange', fullscreenChangeListener.current);\n fullscreenChangeListener.current = null;\n }\n }\n\n useEffect(() => {\n if (videoRef.current) {\n props.player?.mountVideoView(videoRef.current);\n }\n setupFullscreenListener();\n attachAudioNodes();\n\n return () => {\n if (videoRef.current) {\n props.player?.unmountVideoView(videoRef.current);\n }\n cleanupFullscreenListener();\n detachAudioNodes();\n };\n }, [props.player]);\n\n return (\n <video\n controls={props.nativeControls ?? true}\n controlsList={props.allowsFullscreen ? undefined : 'nofullscreen'}\n crossOrigin={props.crossOrigin}\n style={{\n ...mapStyles(props.style),\n objectFit: props.contentFit,\n }}\n onPlay={() => {\n maybeSetupAudioContext();\n }}\n // The player can autoplay when muted, unmuting by a user should create the audio context\n onVolumeChange={() => {\n maybeSetupAudioContext();\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 hasToSetupAudioContext.current = props.useAudioNodePlayback ?? false;\n maybeSetupAudioContext();\n }\n }}\n disablePictureInPicture={!props.allowsPictureInPicture}\n playsInline={props.playsInline}\n src={getSourceUri(props.player?.src) ?? ''}\n />\n );\n});\n\nexport default VideoView;\n"]}
@@ -8,7 +8,7 @@
8
8
  "publication": {
9
9
  "groupId": "host.exp.exponent",
10
10
  "artifactId": "expo.modules.video",
11
- "version": "3.0.3",
11
+ "version": "3.0.5",
12
12
  "repository": "local-maven-repo"
13
13
  }
14
14
  }
@@ -44,11 +44,7 @@ class VideoManager {
44
44
  videoViews.remove(videoView)
45
45
  }
46
46
 
47
- func onAppForegrounded() {
48
- for videoPlayer in videoPlayers.allObjects {
49
- videoPlayer.setTracksEnabled(true)
50
- }
51
- }
47
+ func onAppForegrounded() {}
52
48
 
53
49
  func onAppBackgrounded() {
54
50
  for videoView in videoViews.allObjects {
@@ -56,8 +52,9 @@ class VideoManager {
56
52
  continue
57
53
  }
58
54
  if player.staysActiveInBackground == true {
59
- player.setTracksEnabled(videoView.playerViewController.isInPictureInPicture)
55
+ player.ref.audiovisualBackgroundPlaybackPolicy = .continuesIfPossible
60
56
  } else if !videoView.playerViewController.isInPictureInPicture {
57
+ player.ref.audiovisualBackgroundPlaybackPolicy = .pauses
61
58
  player.ref.pause()
62
59
  }
63
60
  }
@@ -272,23 +272,6 @@ internal final class VideoPlayer: SharedRef<AVPlayer>, Hashable, VideoPlayerObse
272
272
  return
273
273
  }
274
274
 
275
- /**
276
- * iOS automatically pauses videos when the app enters the background. Only way to avoid this is to detach the player from the playerLayer.
277
- * Typical way of doing this for `AVPlayerViewController` is setting `playerViewController.player = nil`, but that makes the
278
- * video invisible for around a second after foregrounding, disabling the tracks requires more code, but works a lot faster.
279
- */
280
- func setTracksEnabled(_ enabled: Bool) {
281
- ref.currentItem?.tracks.forEach({ track in
282
- guard let assetTrack = track.assetTrack else {
283
- return
284
- }
285
-
286
- if assetTrack.hasMediaCharacteristic(AVMediaCharacteristic.visual) {
287
- track.isEnabled = enabled
288
- }
289
- })
290
- }
291
-
292
275
  private func getBufferedPosition() -> Double {
293
276
  guard let currentItem = ref.currentItem else {
294
277
  return -1
@@ -398,7 +398,7 @@ class VideoPlayerObserver: VideoSourceLoaderListener {
398
398
  status = .error
399
399
  }
400
400
 
401
- if let player, !loadedCurrentItem && (status == .readyToPlay || status == .error) {
401
+ if let player, !loadedCurrentItem && (newStatus == .readyToPlay || newStatus == .error) {
402
402
  onLoadedPlayerItem(player: player, playerItem: playerItem)
403
403
  }
404
404
 
@@ -0,0 +1 @@
1
+ 707670539287daf062917fc2972343114fad995d67511c24c60c76fb620c3ee0
@@ -0,0 +1 @@
1
+ d1e577ae947ac37201cd33fe428a6fd9b5ca6fb098380f47ab7f137ee30110b22ba12331bf191c6e43b9b92251171b2c388366013b132d7b5a1e9921aa5d148f
@@ -0,0 +1 @@
1
+ 337e11f92cc2dab64cf71f71cd3ce0a5f9a60f84
@@ -0,0 +1 @@
1
+ 81ef4fc0a33ec845e113a0d85b0476dae1d73ad71e7c2964d07fd28fcad908d0
@@ -0,0 +1 @@
1
+ 4b623b1039f7264c3a34bdb697580be1daad931b0ffd795cf7e2c3543f88bfb171ed3f2e1b8ae297633a3fef8e0c00fcaea8ba7214a829692d264ea889155c52
@@ -3,7 +3,7 @@
3
3
  "component": {
4
4
  "group": "host.exp.exponent",
5
5
  "module": "expo.modules.video",
6
- "version": "3.0.3",
6
+ "version": "3.0.5",
7
7
  "attributes": {
8
8
  "org.gradle.status": "release"
9
9
  }
@@ -24,13 +24,13 @@
24
24
  },
25
25
  "files": [
26
26
  {
27
- "name": "expo.modules.video-3.0.3.aar",
28
- "url": "expo.modules.video-3.0.3.aar",
29
- "size": 485680,
30
- "sha512": "41f22ad6a39595ee69984b0365d34d9f055dafa5ddd10136f8bc504f49f6e6187027d312098b332e3ac0e9291793691e9c6fb1788876cecee1bb70817b465efa",
31
- "sha256": "cb96a0042965654c89f6adce47927f90b45c68af138bf2b2716a5fa92487f49a",
32
- "sha1": "15b7f8125dbc04b715c20812de0eef5d016bbafd",
33
- "md5": "94525837f9ac35c3290ea3f2d51898e8"
27
+ "name": "expo.modules.video-3.0.5.aar",
28
+ "url": "expo.modules.video-3.0.5.aar",
29
+ "size": 485712,
30
+ "sha512": "4b623b1039f7264c3a34bdb697580be1daad931b0ffd795cf7e2c3543f88bfb171ed3f2e1b8ae297633a3fef8e0c00fcaea8ba7214a829692d264ea889155c52",
31
+ "sha256": "81ef4fc0a33ec845e113a0d85b0476dae1d73ad71e7c2964d07fd28fcad908d0",
32
+ "sha1": "337e11f92cc2dab64cf71f71cd3ce0a5f9a60f84",
33
+ "md5": "83ecaeb95367a58583f6e14bc69406f9"
34
34
  }
35
35
  ]
36
36
  },
@@ -113,13 +113,13 @@
113
113
  ],
114
114
  "files": [
115
115
  {
116
- "name": "expo.modules.video-3.0.3.aar",
117
- "url": "expo.modules.video-3.0.3.aar",
118
- "size": 485680,
119
- "sha512": "41f22ad6a39595ee69984b0365d34d9f055dafa5ddd10136f8bc504f49f6e6187027d312098b332e3ac0e9291793691e9c6fb1788876cecee1bb70817b465efa",
120
- "sha256": "cb96a0042965654c89f6adce47927f90b45c68af138bf2b2716a5fa92487f49a",
121
- "sha1": "15b7f8125dbc04b715c20812de0eef5d016bbafd",
122
- "md5": "94525837f9ac35c3290ea3f2d51898e8"
116
+ "name": "expo.modules.video-3.0.5.aar",
117
+ "url": "expo.modules.video-3.0.5.aar",
118
+ "size": 485712,
119
+ "sha512": "4b623b1039f7264c3a34bdb697580be1daad931b0ffd795cf7e2c3543f88bfb171ed3f2e1b8ae297633a3fef8e0c00fcaea8ba7214a829692d264ea889155c52",
120
+ "sha256": "81ef4fc0a33ec845e113a0d85b0476dae1d73ad71e7c2964d07fd28fcad908d0",
121
+ "sha1": "337e11f92cc2dab64cf71f71cd3ce0a5f9a60f84",
122
+ "md5": "83ecaeb95367a58583f6e14bc69406f9"
123
123
  }
124
124
  ]
125
125
  },
@@ -133,13 +133,13 @@
133
133
  },
134
134
  "files": [
135
135
  {
136
- "name": "expo.modules.video-3.0.3-sources.jar",
137
- "url": "expo.modules.video-3.0.3-sources.jar",
138
- "size": 60487,
139
- "sha512": "dda0c9694adade347184346c490319c08eabc8a4f3d19043c73b55184462cde86041b3bd7bd46bf7cc9fd11e2682ec529ba267cc7eaf050abeb54547239e7270",
140
- "sha256": "5d422bb36f52c3620f86f594da1452999395789a361dd1c53d16a3e1e6c746fa",
141
- "sha1": "ab68fa1a321f5c256a72a70f5709ea397d171c02",
142
- "md5": "b7ab1c01aacdaee7b47f6c90d37a3bfa"
136
+ "name": "expo.modules.video-3.0.5-sources.jar",
137
+ "url": "expo.modules.video-3.0.5-sources.jar",
138
+ "size": 60488,
139
+ "sha512": "d1e577ae947ac37201cd33fe428a6fd9b5ca6fb098380f47ab7f137ee30110b22ba12331bf191c6e43b9b92251171b2c388366013b132d7b5a1e9921aa5d148f",
140
+ "sha256": "707670539287daf062917fc2972343114fad995d67511c24c60c76fb620c3ee0",
141
+ "sha1": "3f3a9dcecfb36423985ba8b616a1eb7a810093af",
142
+ "md5": "40a4a05e1ef8a63054f66fb03902dea4"
143
143
  }
144
144
  ]
145
145
  }
@@ -0,0 +1 @@
1
+ d6260dd4678cbbd039726f571f72ce563b0bc808fa00a27caf0521a4d36b79a3
@@ -0,0 +1 @@
1
+ aa8d382e81017c1bcf3360212c31808e518de5f95536687da6e5ae34f676a4c90f433c41d73dbc7a5d48c3591ad5be0031a77007edbe1f74d2c4f2fbc68396e1
@@ -9,7 +9,7 @@
9
9
  <modelVersion>4.0.0</modelVersion>
10
10
  <groupId>host.exp.exponent</groupId>
11
11
  <artifactId>expo.modules.video</artifactId>
12
- <version>3.0.3</version>
12
+ <version>3.0.5</version>
13
13
  <packaging>aar</packaging>
14
14
  <name>expo.modules.video</name>
15
15
  <url>https://github.com/expo/expo</url>
@@ -0,0 +1 @@
1
+ afe1ec2253892a5187cc232eb94fce7d8c1963ed
@@ -0,0 +1 @@
1
+ 4e98712c9f8b1581bfe0e2c3f0bd8ec3640bf25c7354a46b03e2ff03ee66d9ff
@@ -0,0 +1 @@
1
+ b30f6c043c6c4a8459e851a6d4880a5323a1c75fdaaaaf50b86a8ab4f83733ab3aaef4df8961e3aba9482c8612cda24de2909ed01d562d9cb4480c5f61814018
@@ -3,11 +3,11 @@
3
3
  <groupId>host.exp.exponent</groupId>
4
4
  <artifactId>expo.modules.video</artifactId>
5
5
  <versioning>
6
- <latest>3.0.3</latest>
7
- <release>3.0.3</release>
6
+ <latest>3.0.5</latest>
7
+ <release>3.0.5</release>
8
8
  <versions>
9
- <version>3.0.3</version>
9
+ <version>3.0.5</version>
10
10
  </versions>
11
- <lastUpdated>20250821040951</lastUpdated>
11
+ <lastUpdated>20250825161907</lastUpdated>
12
12
  </versioning>
13
13
  </metadata>
@@ -1 +1 @@
1
- 65a516cc3304c2dd71c48e53137ff1d6
1
+ fdf42c6d80bb0bd41b987f33be178d17
@@ -1 +1 @@
1
- 4f6ac8b6d9da296aae326f90acaa47a2cb4266b0
1
+ 547a42db6e869ca392a5ad09f5c1cc41808df351
@@ -1 +1 @@
1
- bc33372698cb038502aaa5373598e24a0b45ae3d6f2b4fefe33158ca94456b95
1
+ 42b3d9562bad2c78f503e7df76169c01e2d86b8dcdb78ad0ff80b3f77ab5ad0e
@@ -1 +1 @@
1
- d16c6fa4db96e48dd0642454b51e4e4e5614dd5b9938df983e9a964d799903db9c94d36a008675c89a6e4695c8fc84d6c26c0f1e5973760000a39a361b3cfc9c
1
+ a4cd3be51cd93e40bb8ba9489fcbb9734f6b156a7d5cf58f86c192de40ff6351926b889ff289e3565c3cefa7d7924e35ef53ce1c476f706f802fb365582010aa
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "expo-video",
3
3
  "title": "Expo Video",
4
- "version": "3.0.3",
4
+ "version": "3.0.5",
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",
@@ -31,12 +31,12 @@
31
31
  "license": "MIT",
32
32
  "dependencies": {},
33
33
  "devDependencies": {
34
- "expo-module-scripts": "^5.0.2"
34
+ "expo-module-scripts": "^5.0.3"
35
35
  },
36
36
  "peerDependencies": {
37
37
  "expo": "*",
38
38
  "react": "*",
39
39
  "react-native": "*"
40
40
  },
41
- "gitHead": "3275774c9286477adba9bc226d3923a996217ee4"
41
+ "gitHead": "ef0b9ecf9645d3e93587d5ee5030dbfcbf735bbd"
42
42
  }
@@ -28,6 +28,8 @@ export interface VideoViewProps extends ViewProps {
28
28
 
29
29
  /**
30
30
  * Determines whether native controls should be displayed or not.
31
+ *
32
+ * > **Note**: Due to platform limitations, the native controls are always enabled in fullscreen mode.
31
33
  * @default true
32
34
  */
33
35
  nativeControls?: boolean;
@@ -172,6 +174,18 @@ export interface VideoViewProps extends ViewProps {
172
174
  * @default undefined
173
175
  */
174
176
  crossOrigin?: 'anonymous' | 'use-credentials';
177
+
178
+ /**
179
+ * Use Audio Nodes for sound playback. When the same player is playing in multiple video views the audio won't increase in volume
180
+ * as the number of players increases.
181
+ *
182
+ * > **Note**: This property is experimental, when enabled it is known to break audio for some sources. Do not change this property at runtime.
183
+ *
184
+ * @experimental
185
+ * @default false
186
+ * @platform web
187
+ */
188
+ useAudioNodePlayback?: boolean;
175
189
  }
176
190
 
177
191
  /**
@@ -44,6 +44,15 @@ export const VideoView = forwardRef((props: { player?: VideoPlayer } & VideoView
44
44
  const audioContextRef = useRef<null | AudioContext>(null);
45
45
  const zeroGainNodeRef = useRef<null | GainNode>(null);
46
46
 
47
+ useEffect(() => {
48
+ if (props.useAudioNodePlayback) {
49
+ maybeSetupAudioContext();
50
+ attachAudioNodes();
51
+ } else {
52
+ detachAudioNodes();
53
+ }
54
+ }, [props.useAudioNodePlayback]);
55
+
47
56
  useImperativeHandle(ref, () => ({
48
57
  enterFullscreen: async () => {
49
58
  if (!props.allowsFullscreen) {
@@ -102,6 +111,9 @@ export const VideoView = forwardRef((props: { player?: VideoPlayer } & VideoView
102
111
  // Adds the video view as a candidate for being the audio source for the player (when multiple views play from one
103
112
  // player only one will emit audio).
104
113
  function attachAudioNodes() {
114
+ if (!props.useAudioNodePlayback) {
115
+ return;
116
+ }
105
117
  const audioContext = audioContextRef.current;
106
118
  const zeroGainNode = zeroGainNodeRef.current;
107
119
  const mediaNode = mediaNodeRef.current;
@@ -116,6 +128,9 @@ export const VideoView = forwardRef((props: { player?: VideoPlayer } & VideoView
116
128
  }
117
129
 
118
130
  function detachAudioNodes() {
131
+ if (!props.useAudioNodePlayback) {
132
+ return;
133
+ }
119
134
  const audioContext = audioContextRef.current;
120
135
  const mediaNode = mediaNodeRef.current;
121
136
  if (audioContext && mediaNode && videoRef.current) {
@@ -127,7 +142,8 @@ export const VideoView = forwardRef((props: { player?: VideoPlayer } & VideoView
127
142
  if (
128
143
  !hasToSetupAudioContext.current ||
129
144
  !navigator.userActivation.hasBeenActive ||
130
- !videoRef.current
145
+ !videoRef.current ||
146
+ !props.useAudioNodePlayback
131
147
  ) {
132
148
  return;
133
149
  }
@@ -200,7 +216,7 @@ export const VideoView = forwardRef((props: { player?: VideoPlayer } & VideoView
200
216
  // we can't assign null to videoRef if we want to unmount it from the player.
201
217
  if (newRef && !newRef.isEqualNode(videoRef.current)) {
202
218
  videoRef.current = newRef;
203
- hasToSetupAudioContext.current = true;
219
+ hasToSetupAudioContext.current = props.useAudioNodePlayback ?? false;
204
220
  maybeSetupAudioContext();
205
221
  }
206
222
  }}
@@ -1 +0,0 @@
1
- 5d422bb36f52c3620f86f594da1452999395789a361dd1c53d16a3e1e6c746fa
@@ -1 +0,0 @@
1
- dda0c9694adade347184346c490319c08eabc8a4f3d19043c73b55184462cde86041b3bd7bd46bf7cc9fd11e2682ec529ba267cc7eaf050abeb54547239e7270
@@ -1 +0,0 @@
1
- 15b7f8125dbc04b715c20812de0eef5d016bbafd
@@ -1 +0,0 @@
1
- cb96a0042965654c89f6adce47927f90b45c68af138bf2b2716a5fa92487f49a
@@ -1 +0,0 @@
1
- 41f22ad6a39595ee69984b0365d34d9f055dafa5ddd10136f8bc504f49f6e6187027d312098b332e3ac0e9291793691e9c6fb1788876cecee1bb70817b465efa
@@ -1 +0,0 @@
1
- f1834c3993411f0e03dd019dae4a44dcc89c763f
@@ -1 +0,0 @@
1
- 7de999c6b107f482fa016ef41cf7c455026a91601c8dc70449a099da8027809d
@@ -1 +0,0 @@
1
- 5f0bd20ba532970a50add3cea8524ae170d28ed305be51cae9964c9e9ed91607a49b4018750cf221d330e65201b1e99aef75d64f64ca7045d77820fdb456ee1d
@@ -1 +0,0 @@
1
- c6c560873a838bab04d2191608cccb2e5507bc3c
@@ -1 +0,0 @@
1
- f5de8d71ebfb96a72a2110f113dfedb7991c4cc4b50ae2c64d4511591b484e15
@@ -1 +0,0 @@
1
- 752a461c0384bc734fbe2311f89f4e2ccffc10e416c5abe709b49b4aa029e3541710d3ddd11326cf772afeec1eeb48ed9ea36f7ca5fb1a4759b535dcdc3deb7c