Merge branch 'master' of https://github.com/react-native-video/react-native-video into feat/add_new_events_on_tracks_changed
# Conflicts: # android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java
This commit is contained in:
		
							
								
								
									
										10
									
								
								.github/ISSUE_TEMPLATE/bug_report.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								.github/ISSUE_TEMPLATE/bug_report.md
									
									
									
									
										vendored
									
									
								
							@@ -10,10 +10,12 @@ assignees: ''
 | 
				
			|||||||
# Bug
 | 
					# Bug
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<!--
 | 
					<!--
 | 
				
			||||||
  Please provide a clear and concise description of what the bug is.
 | 
					  Before opening a ticket
 | 
				
			||||||
  Include screenshots if needed.
 | 
					    * Ensure the issue has not been already reported
 | 
				
			||||||
  Please test using the latest release of the library, as maybe said bug has been already fixed.
 | 
					    * Please test using the latest release of the library, as maybe said bug has been already fixed.
 | 
				
			||||||
  If the library has multiple install methods, describe installation method (e.g., pod, not pod, with jetifier etc)
 | 
					    * Provide a clear and concise description of what the bug is.
 | 
				
			||||||
 | 
					    * If the library has multiple install methods, describe installation method (e.g., pod, not pod, with jetifier etc)
 | 
				
			||||||
 | 
					    * Include screenshots if needed.
 | 
				
			||||||
-->
 | 
					-->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Platform
 | 
					## Platform
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										92
									
								
								API.md
									
									
									
									
									
								
							
							
						
						
									
										92
									
								
								API.md
									
									
									
									
									
								
							@@ -214,7 +214,7 @@ Follow the manual linking instuctions for React Native Windows 0.62 above, but s
 | 
				
			|||||||
 
 | 
					 
 | 
				
			||||||
## Examples
 | 
					## Examples
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Run `yarn xbasic install` before running any of the examples.
 | 
					Run `yarn xbasic install` in the root directory before running any of the examples.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### iOS Example
 | 
					### iOS Example
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
@@ -304,6 +304,7 @@ var styles = StyleSheet.create({
 | 
				
			|||||||
|[selectedTextTrack](#selectedtexttrack)|Android, iOS|
 | 
					|[selectedTextTrack](#selectedtexttrack)|Android, iOS|
 | 
				
			||||||
|[selectedVideoTrack](#selectedvideotrack)|Android|
 | 
					|[selectedVideoTrack](#selectedvideotrack)|Android|
 | 
				
			||||||
|[source](#source)|All|
 | 
					|[source](#source)|All|
 | 
				
			||||||
 | 
					|[subtitleStyle](#subtitleStyle)|Android|
 | 
				
			||||||
|[textTracks](#texttracks)|Android, iOS|
 | 
					|[textTracks](#texttracks)|Android, iOS|
 | 
				
			||||||
|[trackId](#trackId)|Android|
 | 
					|[trackId](#trackId)|Android|
 | 
				
			||||||
|[useTextureView](#usetextureview)|Android|
 | 
					|[useTextureView](#usetextureview)|Android|
 | 
				
			||||||
@@ -347,6 +348,13 @@ var styles = StyleSheet.create({
 | 
				
			|||||||
|[restoreUserInterfaceForPictureInPictureStop](#restoreuserinterfaceforpictureinpicturestop)|iOS|
 | 
					|[restoreUserInterfaceForPictureInPictureStop](#restoreuserinterfaceforpictureinpicturestop)|iOS|
 | 
				
			||||||
|[seek](#seek)|All|
 | 
					|[seek](#seek)|All|
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Static methods
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					| Name |Plateforms Support  |
 | 
				
			||||||
 | 
					|--|--|
 | 
				
			||||||
 | 
					|[getWidevineLevel](#getWidevineLevel)|Android|
 | 
				
			||||||
 | 
					|[isCodecSupported](#isCodecSupported)|Android|
 | 
				
			||||||
 | 
					|[isHEVCSupported](#isHEVCSupported)|Android|
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Configurable props
 | 
					### Configurable props
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -426,9 +434,11 @@ Platforms: Android, iOS
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#### disableFocus
 | 
					#### disableFocus
 | 
				
			||||||
Determines whether video audio should override background music/audio in Android devices.
 | 
					Determines whether video audio should override background music/audio in Android devices.
 | 
				
			||||||
* ** false (default)** - Override background audio/music
 | 
					* **false (default)** - Override background audio/music
 | 
				
			||||||
* **true** - Let background audio/music from other apps play
 | 
					* **true** - Let background audio/music from other apps play
 | 
				
			||||||
 
 | 
					 
 | 
				
			||||||
 | 
					Note: Allows multiple videos to play if set to `true`. If `false`, when one video is playing and another is started, the first video will be paused.
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
Platforms: Android
 | 
					Platforms: Android
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#### disableDisconnectError
 | 
					#### disableDisconnectError
 | 
				
			||||||
@@ -439,7 +449,7 @@ Determines if the player needs to throw an error when connection is lost or not
 | 
				
			|||||||
Platforms: Android
 | 
					Platforms: Android
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### DRM
 | 
					### DRM
 | 
				
			||||||
To setup DRM please follow [this guide](./DRM.md)
 | 
					To setup DRM please follow [this guide](./docs/DRM.md)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Platforms: Android, iOS
 | 
					Platforms: Android, iOS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -866,6 +876,23 @@ The following other types are supported on some platforms, but aren't fully docu
 | 
				
			|||||||
`content://, ms-appx://, ms-appdata://, assets-library://`
 | 
					`content://, ms-appx://, ms-appdata://, assets-library://`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### subtitleStyle
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Property | Description | Platforms
 | 
				
			||||||
 | 
					--- | --- | ---
 | 
				
			||||||
 | 
					fontSizeTrack | Adjust the font size of the subtitles. Default: font size of the device | Android
 | 
				
			||||||
 | 
					paddingTop | Adjust the top padding of the subtitles. Default: 0| Android
 | 
				
			||||||
 | 
					paddingBottom | Adjust the bottom padding of the subtitles. Default: 0| Android
 | 
				
			||||||
 | 
					paddingLeft | Adjust the left padding of the subtitles. Default: 0| Android
 | 
				
			||||||
 | 
					paddingRight | Adjust the right padding of the subtitles. Default: 0| Android
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Example:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					subtitleStyle={{ paddingBottom: 50, fontSize: 20 }}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#### textTracks
 | 
					#### textTracks
 | 
				
			||||||
Load one or more "sidecar" text tracks. This takes an array of objects representing each track. Each object should have the format:
 | 
					Load one or more "sidecar" text tracks. This takes an array of objects representing each track. Each object should have the format:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1427,8 +1454,67 @@ this.player.seek(120, 50); // Seek to 2 minutes with +/- 50 milliseconds accurac
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
Platforms: iOS
 | 
					Platforms: iOS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### Static methods
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Video Decoding capabilities
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					A module embed in ReactNativeVideo allow to query device supported feature.
 | 
				
			||||||
 | 
					To use it include the module as following:
 | 
				
			||||||
 | 
					```javascript
 | 
				
			||||||
 | 
					import { VideoDecoderProperties } from '@ifs/react-native-video-enhanced'
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Platforms: Android
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### getWidevineLevel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Indicates whether the widevine level supported by device.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Possible results:
 | 
				
			||||||
 | 
					-   **0** - unable to determine widevine support (typically not supported)
 | 
				
			||||||
 | 
					-   **1**, **2**, **3** - Widevine level supported
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Platforms: Android
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Example:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					VideoDecoderProperties.getWidevineLevel().then((widevineLevel) => {
 | 
				
			||||||
 | 
					    ...
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### isCodecSupported
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Indicates whether the provided codec is supported level supported by device.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					parameters:
 | 
				
			||||||
 | 
					- **mimetype**: mime type of codec to query
 | 
				
			||||||
 | 
					- **width**, **height**: resolution to query
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Possible results:
 | 
				
			||||||
 | 
					-   **true** - codec supported
 | 
				
			||||||
 | 
					-   **false** - codec is not supported
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Example:
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					VideoDecoderProperties.isCodecSupported('video/avc', 1920, 1080).then(
 | 
				
			||||||
 | 
					    ...
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					Platforms: Android
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### isHEVCSupported
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Helper which Indicates whether the provided HEVC/1920*1080 is supported level supported by device.
 | 
				
			||||||
 | 
					It uses isCodecSupported internally.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Example:
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					VideoDecoderProperties.isHEVCSupported().then((hevcSupported) => {
 | 
				
			||||||
 | 
					    ...
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### iOS App Transport Security
 | 
					### iOS App Transport Security
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,16 +1,25 @@
 | 
				
			|||||||
## Changelog
 | 
					## Changelog
 | 
				
			||||||
 | 
					### Version 6.0.0-alpha3
 | 
				
			||||||
 | 
					- Upgrade ExoPlayer to 2.18.1 [#2846](https://github.com/react-native-video/react-native-video/pull/2846)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Version 6.0.0-alpha.2
 | 
					### Version 6.0.0-alpha.2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Feature add new APIs to query supported features of device decoder (widevine level & codec capabilities) on android [#2740](https://github.com/react-native-video/react-native-video/pull/2740)
 | 
				
			||||||
 | 
					- Feature add support of subtitle styling on android [#2759](https://github.com/react-native-video/react-native-video/pull/2759)
 | 
				
			||||||
 | 
					- Fix Android #2690 ensure onEnd is not sent twice [#2690](https://github.com/react-native-video/react-native-video/issues/2690)
 | 
				
			||||||
- Fix Exoplayer progress not reported when paused [#2664](https://github.com/react-native-video/react-native-video/pull/2664)
 | 
					- Fix Exoplayer progress not reported when paused [#2664](https://github.com/react-native-video/react-native-video/pull/2664)
 | 
				
			||||||
- Call playbackRateChange onPlay and onPause [#1493](https://github.com/react-native-video/react-native-video/pull/1493)
 | 
					- Call playbackRateChange onPlay and onPause [#1493](https://github.com/react-native-video/react-native-video/pull/1493)
 | 
				
			||||||
- Fix being unable to disable sideloaded texttracks in the AVPlayer [#2679](https://github.com/react-native-video/react-native-video/pull/2679)
 | 
					- Fix being unable to disable sideloaded texttracks in the AVPlayer [#2679](https://github.com/react-native-video/react-native-video/pull/2679)
 | 
				
			||||||
- Fixed crash when iOS seek method called reject on the promise [#2743](https://github.com/react-native-video/react-native-video/pull/2743)
 | 
					- Fixed crash when iOS seek method called reject on the promise [#2743](https://github.com/react-native-video/react-native-video/pull/2743)
 | 
				
			||||||
- Fix maxBitRate property being ignored on Android [#2670](https://github.com/react-native-video/react-native-video/pull/2670)
 | 
					- Fix maxBitRate property being ignored on Android [#2670](https://github.com/react-native-video/react-native-video/pull/2670)
 | 
				
			||||||
 | 
					- Fix crash when the source is a cameraroll [#2639] (https://github.com/react-native-video/react-native-video/pull/2639)
 | 
				
			||||||
 | 
					- Fix IOS UI frame drop on loading video [#2848] (https://github.com/react-native-video/react-native-video/pull/2848)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Version 6.0.0-alpha.1
 | 
					### Version 6.0.0-alpha.1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- Remove Android MediaPlayer support [#2724](https://github.com/react-native-video/react-native-video/pull/2724)
 | 
					- Remove Android MediaPlayer support [#2724](https://github.com/react-native-video/react-native-video/pull/2724)
 | 
				
			||||||
 | 
					  **WARNING**: when switching from older version to V6, you need to remove all refrerences of android-exoplayer. This android-exoplayer folder has been renamed to android. Exoplayer is now the only player implementation supported.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- Replace Image.propTypes with ImagePropTypes. [#2718](https://github.com/react-native-video/react-native-video/pull/2718)
 | 
					- Replace Image.propTypes with ImagePropTypes. [#2718](https://github.com/react-native-video/react-native-video/pull/2718)
 | 
				
			||||||
- Fix iOS build caused by type mismatch [#2720](https://github.com/react-native-video/react-native-video/pull/2720)
 | 
					- Fix iOS build caused by type mismatch [#2720](https://github.com/react-native-video/react-native-video/pull/2720)
 | 
				
			||||||
- ERROR TypeError: undefined is not an object (evaluating '_reactNative.Image.propTypes.resizeMode') [#2714](https://github.com/react-native-video/react-native-video/pull/2714)
 | 
					- ERROR TypeError: undefined is not an object (evaluating '_reactNative.Image.propTypes.resizeMode') [#2714](https://github.com/react-native-video/react-native-video/pull/2714)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										12
									
								
								Video.js
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								Video.js
									
									
									
									
									
								
							@@ -14,7 +14,8 @@ const styles = StyleSheet.create({
 | 
				
			|||||||
  },
 | 
					  },
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export { TextTrackType, FilterType, DRMType };
 | 
					const { VideoDecoderProperties } = NativeModules
 | 
				
			||||||
 | 
					export { TextTrackType, FilterType, DRMType, VideoDecoderProperties }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class Video extends Component {
 | 
					export default class Video extends Component {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -302,7 +303,7 @@ export default class Video extends Component {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const isNetwork = !!(uri && uri.match(/^https?:/));
 | 
					    const isNetwork = !!(uri && uri.match(/^https?:/));
 | 
				
			||||||
    const isAsset = !!(uri && uri.match(/^(assets-library|ipod-library|file|content|ms-appx|ms-appdata):/));
 | 
					    const isAsset = !!(uri && uri.match(/^(assets-library|ph|ipod-library|file|content|ms-appx|ms-appdata):/));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let nativeResizeMode;
 | 
					    let nativeResizeMode;
 | 
				
			||||||
    const RCTVideoInstance = this.getViewManagerConfig('RCTVideo');
 | 
					    const RCTVideoInstance = this.getViewManagerConfig('RCTVideo');
 | 
				
			||||||
@@ -510,6 +511,13 @@ Video.propTypes = {
 | 
				
			|||||||
  fullscreenAutorotate: PropTypes.bool,
 | 
					  fullscreenAutorotate: PropTypes.bool,
 | 
				
			||||||
  fullscreenOrientation: PropTypes.oneOf(['all', 'landscape', 'portrait']),
 | 
					  fullscreenOrientation: PropTypes.oneOf(['all', 'landscape', 'portrait']),
 | 
				
			||||||
  progressUpdateInterval: PropTypes.number,
 | 
					  progressUpdateInterval: PropTypes.number,
 | 
				
			||||||
 | 
					  subtitleStyle: PropTypes.shape({
 | 
				
			||||||
 | 
					    paddingTop: PropTypes.number,
 | 
				
			||||||
 | 
					    paddingBottom: PropTypes.number,
 | 
				
			||||||
 | 
					    paddingLeft: PropTypes.number,
 | 
				
			||||||
 | 
					    paddingRight: PropTypes.number,
 | 
				
			||||||
 | 
					    fontSize: PropTypes.number,
 | 
				
			||||||
 | 
					  }),
 | 
				
			||||||
  useTextureView: PropTypes.bool,
 | 
					  useTextureView: PropTypes.bool,
 | 
				
			||||||
  useSecureView: PropTypes.bool,
 | 
					  useSecureView: PropTypes.bool,
 | 
				
			||||||
  hideShutterView: PropTypes.bool,
 | 
					  hideShutterView: PropTypes.bool,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -33,7 +33,7 @@ repositories {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
dependencies {
 | 
					dependencies {
 | 
				
			||||||
    implementation "com.facebook.react:react-native:${safeExtGet('reactNativeVersion', '+')}"
 | 
					    implementation "com.facebook.react:react-native:${safeExtGet('reactNativeVersion', '+')}"
 | 
				
			||||||
    implementation('com.google.android.exoplayer:exoplayer:2.17.1') {
 | 
					    implementation('com.google.android.exoplayer:exoplayer:2.18.1') {
 | 
				
			||||||
        exclude group: 'com.android.support'
 | 
					        exclude group: 'com.android.support'
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -41,8 +41,9 @@ dependencies {
 | 
				
			|||||||
    implementation "androidx.annotation:annotation:1.1.0"
 | 
					    implementation "androidx.annotation:annotation:1.1.0"
 | 
				
			||||||
    implementation "androidx.core:core:1.1.0"
 | 
					    implementation "androidx.core:core:1.1.0"
 | 
				
			||||||
    implementation "androidx.media:media:1.1.0"
 | 
					    implementation "androidx.media:media:1.1.0"
 | 
				
			||||||
 | 
					    implementation 'androidx.activity:activity:1.4.0'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    implementation('com.google.android.exoplayer:extension-okhttp:2.17.1') {
 | 
					    implementation('com.google.android.exoplayer:extension-okhttp:2.18.1') {
 | 
				
			||||||
        exclude group: 'com.squareup.okhttp3', module: 'okhttp'
 | 
					        exclude group: 'com.squareup.okhttp3', module: 'okhttp'
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    implementation 'com.squareup.okhttp3:okhttp:${OKHTTP_VERSION}'
 | 
					    implementation 'com.squareup.okhttp3:okhttp:${OKHTTP_VERSION}'
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										22
									
								
								android/src/main/java/com/brentvatne/ReactBridgeUtils.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								android/src/main/java/com/brentvatne/ReactBridgeUtils.java
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
				
			|||||||
 | 
					package com.brentvatne;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.facebook.react.bridge.ReadableMap;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					* This file define static helpers to parse in an easier way input props
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class ReactBridgeUtils {
 | 
				
			||||||
 | 
					    /*
 | 
				
			||||||
 | 
					    retrieve key from map as int. fallback is returned if not available
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    static public int safeGetInt(ReadableMap map, String key, int fallback) {
 | 
				
			||||||
 | 
					        return map != null && map.hasKey(key) && !map.isNull(key) ? map.getInt(key) : fallback;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /*
 | 
				
			||||||
 | 
					    retrieve key from map as double. fallback is returned if not available
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    static public double safeGetDouble(ReadableMap map, String key, double fallback) {
 | 
				
			||||||
 | 
					        return map != null && map.hasKey(key) && !map.isNull(key) ? map.getDouble(key) : fallback;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -5,6 +5,7 @@ import android.content.Context;
 | 
				
			|||||||
import androidx.core.content.ContextCompat;
 | 
					import androidx.core.content.ContextCompat;
 | 
				
			||||||
import android.util.AttributeSet;
 | 
					import android.util.AttributeSet;
 | 
				
			||||||
import android.util.Log;
 | 
					import android.util.Log;
 | 
				
			||||||
 | 
					import android.util.TypedValue;
 | 
				
			||||||
import android.view.Gravity;
 | 
					import android.view.Gravity;
 | 
				
			||||||
import android.view.SurfaceView;
 | 
					import android.view.SurfaceView;
 | 
				
			||||||
import android.view.TextureView;
 | 
					import android.view.TextureView;
 | 
				
			||||||
@@ -18,7 +19,7 @@ import com.google.android.exoplayer2.PlaybackParameters;
 | 
				
			|||||||
import com.google.android.exoplayer2.Player;
 | 
					import com.google.android.exoplayer2.Player;
 | 
				
			||||||
import com.google.android.exoplayer2.ExoPlayer;
 | 
					import com.google.android.exoplayer2.ExoPlayer;
 | 
				
			||||||
import com.google.android.exoplayer2.Timeline;
 | 
					import com.google.android.exoplayer2.Timeline;
 | 
				
			||||||
import com.google.android.exoplayer2.TracksInfo;
 | 
					import com.google.android.exoplayer2.Tracks;
 | 
				
			||||||
import com.google.android.exoplayer2.text.Cue;
 | 
					import com.google.android.exoplayer2.text.Cue;
 | 
				
			||||||
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
 | 
					import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
 | 
				
			||||||
import com.google.android.exoplayer2.ui.SubtitleView;
 | 
					import com.google.android.exoplayer2.ui.SubtitleView;
 | 
				
			||||||
@@ -100,6 +101,16 @@ public final class ExoPlayerView extends FrameLayout {
 | 
				
			|||||||
            player.setVideoSurfaceView((SurfaceView) surfaceView);
 | 
					            player.setVideoSurfaceView((SurfaceView) surfaceView);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    public void setSubtitleStyle(SubtitleStyle style) {
 | 
				
			||||||
 | 
					        // ensure we reset subtile style before reapplying it
 | 
				
			||||||
 | 
					        subtitleLayout.setUserDefaultStyle();
 | 
				
			||||||
 | 
					        subtitleLayout.setUserDefaultTextSize();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (style.getFontSize() > 0) {
 | 
				
			||||||
 | 
					            subtitleLayout.setFixedTextSize(TypedValue.COMPLEX_UNIT_SP, style.getFontSize());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        subtitleLayout.setPadding(style.getPaddingLeft(), style.getPaddingTop(), style.getPaddingRight(), style.getPaddingBottom());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private void updateSurfaceView() {
 | 
					    private void updateSurfaceView() {
 | 
				
			||||||
        View view;
 | 
					        View view;
 | 
				
			||||||
@@ -230,7 +241,7 @@ public final class ExoPlayerView extends FrameLayout {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        @Override
 | 
					        @Override
 | 
				
			||||||
        public void onCues(List<Cue> cues) {
 | 
					        public void onCues(List<Cue> cues) {
 | 
				
			||||||
            subtitleLayout.onCues(cues);
 | 
					            subtitleLayout.setCues(cues);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // ExoPlayer.VideoListener implementation
 | 
					        // ExoPlayer.VideoListener implementation
 | 
				
			||||||
@@ -284,7 +295,7 @@ public final class ExoPlayerView extends FrameLayout {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        @Override
 | 
					        @Override
 | 
				
			||||||
        public void onTracksInfoChanged(TracksInfo tracksInfo) {
 | 
					        public void onTracksChanged(Tracks tracks) {
 | 
				
			||||||
            updateForCurrentTrackSelections();
 | 
					            updateForCurrentTrackSelections();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,80 @@
 | 
				
			|||||||
 | 
					package com.brentvatne.exoplayer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import android.app.Dialog;
 | 
				
			||||||
 | 
					import android.content.Context;
 | 
				
			||||||
 | 
					import android.view.ViewGroup;
 | 
				
			||||||
 | 
					import android.widget.FrameLayout;
 | 
				
			||||||
 | 
					import android.widget.ImageButton;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import androidx.activity.OnBackPressedCallback;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.google.android.exoplayer2.ui.PlayerControlView;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class FullScreenPlayerView extends Dialog {
 | 
				
			||||||
 | 
					  private final PlayerControlView playerControlView;
 | 
				
			||||||
 | 
					  private final ExoPlayerView exoPlayerView;
 | 
				
			||||||
 | 
					  private ViewGroup parent;
 | 
				
			||||||
 | 
					  private final FrameLayout containerView;
 | 
				
			||||||
 | 
					  private final OnBackPressedCallback onBackPressedCallback;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public FullScreenPlayerView(Context context, ExoPlayerView exoPlayerView, PlayerControlView playerControlView, OnBackPressedCallback onBackPressedCallback) {
 | 
				
			||||||
 | 
					    super(context, android.R.style.Theme_Black_NoTitleBar_Fullscreen);
 | 
				
			||||||
 | 
					    this.playerControlView = playerControlView;
 | 
				
			||||||
 | 
					    this.exoPlayerView = exoPlayerView;
 | 
				
			||||||
 | 
					    this.onBackPressedCallback = onBackPressedCallback;
 | 
				
			||||||
 | 
					    containerView = new FrameLayout(context);
 | 
				
			||||||
 | 
					    setContentView(containerView, generateDefaultLayoutParams());
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public void onBackPressed() {
 | 
				
			||||||
 | 
					    super.onBackPressed();
 | 
				
			||||||
 | 
					    onBackPressedCallback.handleOnBackPressed();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  protected void onStart() {
 | 
				
			||||||
 | 
					    parent = (FrameLayout)(exoPlayerView.getParent());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    parent.removeView(exoPlayerView);
 | 
				
			||||||
 | 
					    containerView.addView(exoPlayerView, generateDefaultLayoutParams());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (playerControlView != null) {
 | 
				
			||||||
 | 
					      ImageButton imageButton = playerControlView.findViewById(com.brentvatne.react.R.id.exo_fullscreen);
 | 
				
			||||||
 | 
					      imageButton.setImageResource(com.google.android.exoplayer2.ui.R.drawable.exo_icon_fullscreen_exit);
 | 
				
			||||||
 | 
					      imageButton.setContentDescription(getContext().getString(com.google.android.exoplayer2.ui.R.string.exo_controls_fullscreen_exit_description));
 | 
				
			||||||
 | 
					      parent.removeView(playerControlView);
 | 
				
			||||||
 | 
					      containerView.addView(playerControlView, generateDefaultLayoutParams());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    super.onStart();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  protected void onStop() {
 | 
				
			||||||
 | 
					    containerView.removeView(exoPlayerView);
 | 
				
			||||||
 | 
					    parent.addView(exoPlayerView, generateDefaultLayoutParams());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (playerControlView != null) {
 | 
				
			||||||
 | 
					      ImageButton imageButton = playerControlView.findViewById(com.brentvatne.react.R.id.exo_fullscreen);
 | 
				
			||||||
 | 
					      imageButton.setImageResource(com.google.android.exoplayer2.ui.R.drawable.exo_icon_fullscreen_enter);
 | 
				
			||||||
 | 
					      imageButton.setContentDescription(getContext().getString(com.google.android.exoplayer2.ui.R.string.exo_controls_fullscreen_enter_description));
 | 
				
			||||||
 | 
					      containerView.removeView(playerControlView);
 | 
				
			||||||
 | 
					      parent.addView(playerControlView, generateDefaultLayoutParams());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    parent.requestLayout();
 | 
				
			||||||
 | 
					    parent = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    super.onStop();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private FrameLayout.LayoutParams generateDefaultLayoutParams() {
 | 
				
			||||||
 | 
					    FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(
 | 
				
			||||||
 | 
					        FrameLayout.LayoutParams.MATCH_PARENT,
 | 
				
			||||||
 | 
					        FrameLayout.LayoutParams.MATCH_PARENT
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    layoutParams.setMargins(0, 0, 0, 0);
 | 
				
			||||||
 | 
					    return layoutParams;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -18,6 +18,7 @@ import android.widget.FrameLayout;
 | 
				
			|||||||
import android.widget.ImageButton;
 | 
					import android.widget.ImageButton;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import androidx.annotation.WorkerThread;
 | 
					import androidx.annotation.WorkerThread;
 | 
				
			||||||
 | 
					import androidx.activity.OnBackPressedCallback;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.brentvatne.common.Track;
 | 
					import com.brentvatne.common.Track;
 | 
				
			||||||
import com.brentvatne.common.VideoTrack;
 | 
					import com.brentvatne.common.VideoTrack;
 | 
				
			||||||
@@ -39,8 +40,9 @@ import com.google.android.exoplayer2.PlaybackParameters;
 | 
				
			|||||||
import com.google.android.exoplayer2.Player;
 | 
					import com.google.android.exoplayer2.Player;
 | 
				
			||||||
import com.google.android.exoplayer2.ExoPlayer;
 | 
					import com.google.android.exoplayer2.ExoPlayer;
 | 
				
			||||||
import com.google.android.exoplayer2.Timeline;
 | 
					import com.google.android.exoplayer2.Timeline;
 | 
				
			||||||
import com.google.android.exoplayer2.TracksInfo;
 | 
					import com.google.android.exoplayer2.Tracks;
 | 
				
			||||||
import com.google.android.exoplayer2.drm.DefaultDrmSessionManager;
 | 
					import com.google.android.exoplayer2.drm.DefaultDrmSessionManager;
 | 
				
			||||||
 | 
					import com.google.android.exoplayer2.drm.DefaultDrmSessionManagerProvider;
 | 
				
			||||||
import com.google.android.exoplayer2.drm.DrmSessionEventListener;
 | 
					import com.google.android.exoplayer2.drm.DrmSessionEventListener;
 | 
				
			||||||
import com.google.android.exoplayer2.drm.DrmSessionManager;
 | 
					import com.google.android.exoplayer2.drm.DrmSessionManager;
 | 
				
			||||||
import com.google.android.exoplayer2.drm.DrmSessionManagerProvider;
 | 
					import com.google.android.exoplayer2.drm.DrmSessionManagerProvider;
 | 
				
			||||||
@@ -129,6 +131,7 @@ class ReactExoplayerView extends FrameLayout implements
 | 
				
			|||||||
    private Player.Listener eventListener;
 | 
					    private Player.Listener eventListener;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private ExoPlayerView exoPlayerView;
 | 
					    private ExoPlayerView exoPlayerView;
 | 
				
			||||||
 | 
					    private FullScreenPlayerView fullScreenPlayerView;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private DataSource.Factory mediaDataSourceFactory;
 | 
					    private DataSource.Factory mediaDataSourceFactory;
 | 
				
			||||||
    private ExoPlayer player;
 | 
					    private ExoPlayer player;
 | 
				
			||||||
@@ -356,7 +359,6 @@ class ReactExoplayerView extends FrameLayout implements
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        // Setting the player for the playerControlView
 | 
					        // Setting the player for the playerControlView
 | 
				
			||||||
        playerControlView.setPlayer(player);
 | 
					        playerControlView.setPlayer(player);
 | 
				
			||||||
        playerControlView.show();
 | 
					 | 
				
			||||||
        playPauseControlContainer = playerControlView.findViewById(R.id.exo_play_pause_container);
 | 
					        playPauseControlContainer = playerControlView.findViewById(R.id.exo_play_pause_container);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Invoking onClick event for exoplayerView
 | 
					        // Invoking onClick event for exoplayerView
 | 
				
			||||||
@@ -388,6 +390,10 @@ class ReactExoplayerView extends FrameLayout implements
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        //Handling the fullScreenButton click event
 | 
				
			||||||
 | 
					        ImageButton fullScreenButton = playerControlView.findViewById(R.id.exo_fullscreen);
 | 
				
			||||||
 | 
					        fullScreenButton.setOnClickListener(v -> setFullscreen(!isFullscreen));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Invoking onPlaybackStateChanged and onPlayWhenReadyChanged events for Player
 | 
					        // Invoking onPlaybackStateChanged and onPlayWhenReadyChanged events for Player
 | 
				
			||||||
        eventListener = new Player.Listener() {
 | 
					        eventListener = new Player.Listener() {
 | 
				
			||||||
            @Override
 | 
					            @Override
 | 
				
			||||||
@@ -430,6 +436,7 @@ class ReactExoplayerView extends FrameLayout implements
 | 
				
			|||||||
            removeViewAt(indexOfPC);
 | 
					            removeViewAt(indexOfPC);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        addView(playerControlView, 1, layoutParams);
 | 
					        addView(playerControlView, 1, layoutParams);
 | 
				
			||||||
 | 
					        reLayout(playerControlView);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
@@ -557,7 +564,7 @@ class ReactExoplayerView extends FrameLayout implements
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    private void initializePlayerCore(ReactExoplayerView self) {
 | 
					    private void initializePlayerCore(ReactExoplayerView self) {
 | 
				
			||||||
        ExoTrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory();
 | 
					        ExoTrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory();
 | 
				
			||||||
        self.trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory);
 | 
					        self.trackSelector = new DefaultTrackSelector(getContext(), videoTrackSelectionFactory);
 | 
				
			||||||
        self.trackSelector.setParameters(trackSelector.buildUponParameters()
 | 
					        self.trackSelector.setParameters(trackSelector.buildUponParameters()
 | 
				
			||||||
                .setMaxVideoBitrate(maxBitRate == 0 ? Integer.MAX_VALUE : maxBitRate));
 | 
					                .setMaxVideoBitrate(maxBitRate == 0 ? Integer.MAX_VALUE : maxBitRate));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -653,6 +660,12 @@ class ReactExoplayerView extends FrameLayout implements
 | 
				
			|||||||
        setControls(controls);
 | 
					        setControls(controls);
 | 
				
			||||||
        applyModifiers();
 | 
					        applyModifiers();
 | 
				
			||||||
        startBufferCheckTimer();
 | 
					        startBufferCheckTimer();
 | 
				
			||||||
 | 
					        fullScreenPlayerView = new FullScreenPlayerView(getContext(), exoPlayerView, playerControlView, new OnBackPressedCallback(true) {
 | 
				
			||||||
 | 
					            @Override
 | 
				
			||||||
 | 
					            public void handleOnBackPressed() {
 | 
				
			||||||
 | 
					                setFullscreen(false);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private DrmSessionManager buildDrmSessionManager(UUID uuid, String licenseUrl, String[] keyRequestPropertiesArray) throws UnsupportedDrmException {
 | 
					    private DrmSessionManager buildDrmSessionManager(UUID uuid, String licenseUrl, String[] keyRequestPropertiesArray) throws UnsupportedDrmException {
 | 
				
			||||||
@@ -707,6 +720,8 @@ class ReactExoplayerView extends FrameLayout implements
 | 
				
			|||||||
                    return drmSessionManager;
 | 
					                    return drmSessionManager;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            drmProvider = new DefaultDrmSessionManagerProvider();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        switch (type) {
 | 
					        switch (type) {
 | 
				
			||||||
            case C.TYPE_SS:
 | 
					            case C.TYPE_SS:
 | 
				
			||||||
@@ -813,7 +828,10 @@ class ReactExoplayerView extends FrameLayout implements
 | 
				
			|||||||
                player.setPlayWhenReady(true);
 | 
					                player.setPlayWhenReady(true);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            player.setPlayWhenReady(false);
 | 
					            // ensure playback is not ENDED, else it will trigger another ended event
 | 
				
			||||||
 | 
					            if (player.getPlaybackState() != Player.STATE_ENDED) {
 | 
				
			||||||
 | 
					                player.setPlayWhenReady(false);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1290,11 +1308,10 @@ class ReactExoplayerView extends FrameLayout implements
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public void onTracksInfoChanged(TracksInfo tracksInfo) {
 | 
					    public void onTracksChanged(Tracks tracks) {
 | 
				
			||||||
        eventEmitter.textTracks(getTextTrackInfo());
 | 
					        eventEmitter.textTracks(getTextTrackInfo());
 | 
				
			||||||
        eventEmitter.audioTracks(getAudioTrackInfo());
 | 
					        eventEmitter.audioTracks(getAudioTrackInfo());
 | 
				
			||||||
        eventEmitter.videoTracks(getVideoTrackInfo());
 | 
					        eventEmitter.videoTracks(getVideoTrackInfo());
 | 
				
			||||||
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
@@ -1605,10 +1622,10 @@ class ReactExoplayerView extends FrameLayout implements
 | 
				
			|||||||
        TrackSelectionOverride selectionOverride = new TrackSelectionOverride(groups.get(groupIndex), tracks);
 | 
					        TrackSelectionOverride selectionOverride = new TrackSelectionOverride(groups.get(groupIndex), tracks);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        DefaultTrackSelector.Parameters selectionParameters = trackSelector.getParameters()
 | 
					        DefaultTrackSelector.Parameters selectionParameters = trackSelector.getParameters()
 | 
				
			||||||
                .buildUpon()
 | 
					            .buildUpon()
 | 
				
			||||||
                .setRendererDisabled(rendererIndex, false)
 | 
					            .setRendererDisabled(rendererIndex, false)
 | 
				
			||||||
                .setTrackSelectionOverrides(new TrackSelectionOverrides.Builder().addOverride(selectionOverride).build())
 | 
					            .addOverride(selectionOverride)
 | 
				
			||||||
                .build();
 | 
					            .build();
 | 
				
			||||||
        trackSelector.setParameters(selectionParameters);
 | 
					        trackSelector.setParameters(selectionParameters);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1767,6 +1784,16 @@ class ReactExoplayerView extends FrameLayout implements
 | 
				
			|||||||
        if (activity == null) {
 | 
					        if (activity == null) {
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (fullScreenPlayerView == null) {
 | 
				
			||||||
 | 
					            fullScreenPlayerView = new FullScreenPlayerView(getContext(), exoPlayerView, playerControlView, new OnBackPressedCallback(true) {
 | 
				
			||||||
 | 
					                @Override
 | 
				
			||||||
 | 
					                public void handleOnBackPressed() {
 | 
				
			||||||
 | 
					                    setFullscreen(false);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Window window = activity.getWindow();
 | 
					        Window window = activity.getWindow();
 | 
				
			||||||
        View decorView = window.getDecorView();
 | 
					        View decorView = window.getDecorView();
 | 
				
			||||||
        int uiOptions;
 | 
					        int uiOptions;
 | 
				
			||||||
@@ -1780,13 +1807,24 @@ class ReactExoplayerView extends FrameLayout implements
 | 
				
			|||||||
                        | SYSTEM_UI_FLAG_FULLSCREEN;
 | 
					                        | SYSTEM_UI_FLAG_FULLSCREEN;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            eventEmitter.fullscreenWillPresent();
 | 
					            eventEmitter.fullscreenWillPresent();
 | 
				
			||||||
            decorView.setSystemUiVisibility(uiOptions);
 | 
					            post(() -> {
 | 
				
			||||||
            eventEmitter.fullscreenDidPresent();
 | 
					                decorView.setSystemUiVisibility(uiOptions);
 | 
				
			||||||
 | 
					                if (controls) {
 | 
				
			||||||
 | 
					                    fullScreenPlayerView.show();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                eventEmitter.fullscreenDidPresent();
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            uiOptions = View.SYSTEM_UI_FLAG_VISIBLE;
 | 
					            uiOptions = View.SYSTEM_UI_FLAG_VISIBLE;
 | 
				
			||||||
            eventEmitter.fullscreenWillDismiss();
 | 
					            eventEmitter.fullscreenWillDismiss();
 | 
				
			||||||
            decorView.setSystemUiVisibility(uiOptions);
 | 
					            post(() -> {
 | 
				
			||||||
            eventEmitter.fullscreenDidDismiss();
 | 
					                decorView.setSystemUiVisibility(uiOptions);
 | 
				
			||||||
 | 
					                if (controls) {
 | 
				
			||||||
 | 
					                    fullScreenPlayerView.dismiss();
 | 
				
			||||||
 | 
					                    reLayout(exoPlayerView);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                eventEmitter.fullscreenDidDismiss();
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1866,4 +1904,8 @@ class ReactExoplayerView extends FrameLayout implements
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setSubtitleStyle(SubtitleStyle style) {
 | 
				
			||||||
 | 
					        exoPlayerView.setSubtitleStyle(style);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -79,6 +79,8 @@ public class ReactExoplayerViewManager extends ViewGroupManager<ReactExoplayerVi
 | 
				
			|||||||
    private static final String PROP_HIDE_SHUTTER_VIEW = "hideShutterView";
 | 
					    private static final String PROP_HIDE_SHUTTER_VIEW = "hideShutterView";
 | 
				
			||||||
    private static final String PROP_CONTROLS = "controls";
 | 
					    private static final String PROP_CONTROLS = "controls";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static final String PROP_SUBTITLE_STYLE = "subtitleStyle";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private ReactExoplayerConfig config;
 | 
					    private ReactExoplayerConfig config;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public ReactExoplayerViewManager(ReactExoplayerConfig config) {
 | 
					    public ReactExoplayerViewManager(ReactExoplayerConfig config) {
 | 
				
			||||||
@@ -347,6 +349,11 @@ public class ReactExoplayerViewManager extends ViewGroupManager<ReactExoplayerVi
 | 
				
			|||||||
        videoView.setControls(controls);
 | 
					        videoView.setControls(controls);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @ReactProp(name = PROP_SUBTITLE_STYLE)
 | 
				
			||||||
 | 
					    public void setSubtitleStyle(final ReactExoplayerView videoView, @Nullable final ReadableMap src) {
 | 
				
			||||||
 | 
					        videoView.setSubtitleStyle(SubtitleStyle.parse(src));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @ReactProp(name = PROP_BUFFER_CONFIG)
 | 
					    @ReactProp(name = PROP_BUFFER_CONFIG)
 | 
				
			||||||
    public void setBufferConfig(final ReactExoplayerView videoView, @Nullable ReadableMap bufferConfig) {
 | 
					    public void setBufferConfig(final ReactExoplayerView videoView, @Nullable ReadableMap bufferConfig) {
 | 
				
			||||||
        int minBufferMs = DefaultLoadControl.DEFAULT_MIN_BUFFER_MS;
 | 
					        int minBufferMs = DefaultLoadControl.DEFAULT_MIN_BUFFER_MS;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,38 @@
 | 
				
			|||||||
 | 
					package com.brentvatne.exoplayer;
 | 
				
			||||||
 | 
					import com.brentvatne.ReactBridgeUtils;
 | 
				
			||||||
 | 
					import com.facebook.react.bridge.ReadableMap;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Helper file to parse SubtitleStyle prop and build a dedicated class
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class SubtitleStyle {
 | 
				
			||||||
 | 
					    private static final String PROP_FONT_SIZE_TRACK = "fontSize";
 | 
				
			||||||
 | 
					    private static final String PROP_PADDING_BOTTOM = "paddingBottom";
 | 
				
			||||||
 | 
					    private static final String PROP_PADDING_TOP = "paddingTop";
 | 
				
			||||||
 | 
					    private static final String PROP_PADDING_LEFT = "paddingLeft";
 | 
				
			||||||
 | 
					    private static final String PROP_PADDING_RIGHT = "paddingRight";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private int fontSize = -1;
 | 
				
			||||||
 | 
					    private int paddingLeft = 0;
 | 
				
			||||||
 | 
					    private int paddingRight = 0;
 | 
				
			||||||
 | 
					    private int paddingTop = 0;
 | 
				
			||||||
 | 
					    private int paddingBottom = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private SubtitleStyle() {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    int getFontSize() {return fontSize;}
 | 
				
			||||||
 | 
					    int getPaddingBottom() {return paddingBottom;}
 | 
				
			||||||
 | 
					    int getPaddingTop() {return paddingTop;}
 | 
				
			||||||
 | 
					    int getPaddingLeft() {return paddingLeft;}
 | 
				
			||||||
 | 
					    int getPaddingRight() {return paddingRight;}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static SubtitleStyle parse(ReadableMap src) {
 | 
				
			||||||
 | 
					        SubtitleStyle subtitleStyle = new SubtitleStyle();
 | 
				
			||||||
 | 
					        subtitleStyle.fontSize = ReactBridgeUtils.safeGetInt(src, PROP_FONT_SIZE_TRACK, -1);
 | 
				
			||||||
 | 
					        subtitleStyle.paddingBottom = ReactBridgeUtils.safeGetInt(src, PROP_PADDING_BOTTOM, 0);
 | 
				
			||||||
 | 
					        subtitleStyle.paddingTop = ReactBridgeUtils.safeGetInt(src, PROP_PADDING_TOP, 0);
 | 
				
			||||||
 | 
					        subtitleStyle.paddingLeft = ReactBridgeUtils.safeGetInt(src, PROP_PADDING_LEFT, 0);
 | 
				
			||||||
 | 
					        subtitleStyle.paddingRight = ReactBridgeUtils.safeGetInt(src, PROP_PADDING_RIGHT, 0);
 | 
				
			||||||
 | 
					        return subtitleStyle;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -25,7 +25,9 @@ public class ReactVideoPackage implements ReactPackage {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
 | 
					    public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
 | 
				
			||||||
        return Collections.emptyList();
 | 
					        return Collections.singletonList(
 | 
				
			||||||
 | 
					                new VideoDecoderPropertiesModule(reactContext)
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Deprecated RN 0.47
 | 
					    // Deprecated RN 0.47
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,107 @@
 | 
				
			|||||||
 | 
					package com.brentvatne.react;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import android.annotation.SuppressLint;
 | 
				
			||||||
 | 
					import android.media.MediaCodecList;
 | 
				
			||||||
 | 
					import android.media.MediaDrm;
 | 
				
			||||||
 | 
					import android.media.MediaFormat;
 | 
				
			||||||
 | 
					import android.media.UnsupportedSchemeException;
 | 
				
			||||||
 | 
					import android.os.Build;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import androidx.annotation.NonNull;
 | 
				
			||||||
 | 
					import androidx.annotation.RequiresApi;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.facebook.react.bridge.Promise;
 | 
				
			||||||
 | 
					import com.facebook.react.bridge.ReactApplicationContext;
 | 
				
			||||||
 | 
					import com.facebook.react.bridge.ReactContextBaseJavaModule;
 | 
				
			||||||
 | 
					import com.facebook.react.bridge.ReactMethod;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.UUID;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
 | 
				
			||||||
 | 
					public class VideoDecoderPropertiesModule extends ReactContextBaseJavaModule {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ReactApplicationContext reactContext;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @NonNull
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public String getName() {
 | 
				
			||||||
 | 
					        return "VideoDecoderProperties";
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @SuppressLint("ObsoleteSdkInt")
 | 
				
			||||||
 | 
					    @ReactMethod
 | 
				
			||||||
 | 
					    public void getWidevineLevel(Promise p) {
 | 
				
			||||||
 | 
					        int widevineLevel = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.JELLY_BEAN_MR2) {
 | 
				
			||||||
 | 
					            p.resolve(widevineLevel);
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        final UUID WIDEVINE_UUID = new UUID(0xEDEF8BA979D64ACEL, 0xA3C827DCD51D21EDL);
 | 
				
			||||||
 | 
					        final String WIDEVINE_SECURITY_LEVEL_1 = "L1";
 | 
				
			||||||
 | 
					        final String WIDEVINE_SECURITY_LEVEL_2 = "L2";
 | 
				
			||||||
 | 
					        final String WIDEVINE_SECURITY_LEVEL_3 = "L3";
 | 
				
			||||||
 | 
					        final String SECURITY_LEVEL_PROPERTY = "securityLevel";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        String securityProperty = null;
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            MediaDrm mediaDrm = new MediaDrm(WIDEVINE_UUID);
 | 
				
			||||||
 | 
					            securityProperty = mediaDrm.getPropertyString(SECURITY_LEVEL_PROPERTY);
 | 
				
			||||||
 | 
					        } catch (UnsupportedSchemeException e) {
 | 
				
			||||||
 | 
					            e.printStackTrace();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (securityProperty == null) {
 | 
				
			||||||
 | 
					            p.resolve(widevineLevel);
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        switch (securityProperty) {
 | 
				
			||||||
 | 
					            case WIDEVINE_SECURITY_LEVEL_1: {
 | 
				
			||||||
 | 
					                widevineLevel = 1;
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            case WIDEVINE_SECURITY_LEVEL_2: {
 | 
				
			||||||
 | 
					                widevineLevel = 2;
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            case WIDEVINE_SECURITY_LEVEL_3: {
 | 
				
			||||||
 | 
					                widevineLevel = 3;
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            default: {
 | 
				
			||||||
 | 
					                // widevineLevel 0
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        p.resolve(widevineLevel);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @SuppressLint("ObsoleteSdkInt")
 | 
				
			||||||
 | 
					    @ReactMethod
 | 
				
			||||||
 | 
					    public void isCodecSupported(String mimeType, int width, int height, Promise p) {
 | 
				
			||||||
 | 
					        if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.LOLLIPOP) {
 | 
				
			||||||
 | 
					            p.resolve(false);
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        MediaCodecList mRegularCodecs = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
 | 
				
			||||||
 | 
					        MediaFormat format = MediaFormat.createVideoFormat(mimeType, width, height);
 | 
				
			||||||
 | 
					        String codecName = mRegularCodecs.findDecoderForFormat(format);
 | 
				
			||||||
 | 
					        if (codecName == null) {
 | 
				
			||||||
 | 
					            p.resolve(false);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            p.resolve(true);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @ReactMethod
 | 
				
			||||||
 | 
					    public void isHEVCSupported(Promise p) {
 | 
				
			||||||
 | 
					        isCodecSupported("video/hevc", 1920, 1080, p);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public VideoDecoderPropertiesModule(ReactApplicationContext reactContext) {
 | 
				
			||||||
 | 
					        super(reactContext);
 | 
				
			||||||
 | 
					        this.reactContext = reactContext;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -71,6 +71,14 @@
 | 
				
			|||||||
            android:paddingRight="4dp"
 | 
					            android:paddingRight="4dp"
 | 
				
			||||||
            android:includeFontPadding="false"
 | 
					            android:includeFontPadding="false"
 | 
				
			||||||
            android:textColor="#FFBEBEBE"/>
 | 
					            android:textColor="#FFBEBEBE"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <ImageButton
 | 
				
			||||||
 | 
					            android:id="@id/exo_fullscreen"
 | 
				
			||||||
 | 
					            style="@style/ExoMediaButton.FullScreen"
 | 
				
			||||||
 | 
					            android:layout_width="30dp"
 | 
				
			||||||
 | 
					            android:layout_height="30dp"
 | 
				
			||||||
 | 
					            android:layout_margin="4dp"
 | 
				
			||||||
 | 
					            android:scaleType="fitCenter" />
 | 
				
			||||||
    </LinearLayout>
 | 
					    </LinearLayout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
</LinearLayout>
 | 
					</LinearLayout>
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										7
									
								
								android/src/main/res/values/styles.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								android/src/main/res/values/styles.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="utf-8"?>
 | 
				
			||||||
 | 
					<resources>
 | 
				
			||||||
 | 
					    <style name="ExoMediaButton.FullScreen">
 | 
				
			||||||
 | 
					        <item name="android:src">@drawable/exo_icon_fullscreen_enter</item>
 | 
				
			||||||
 | 
					        <item name="android:contentDescription">@string/exo_controls_fullscreen_enter_description</item>
 | 
				
			||||||
 | 
					    </style>
 | 
				
			||||||
 | 
					</resources>
 | 
				
			||||||
@@ -16,7 +16,7 @@ import {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import { Picker } from '@react-native-picker/picker'
 | 
					import { Picker } from '@react-native-picker/picker'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import Video, { TextTrackType } from 'react-native-video';
 | 
					import Video, { VideoDecoderProperties, TextTrackType } from 'react-native-video';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class VideoPlayer extends Component {
 | 
					class VideoPlayer extends Component {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -77,6 +77,28 @@ class VideoPlayer extends Component {
 | 
				
			|||||||
  video: Video;
 | 
					  video: Video;
 | 
				
			||||||
  seekPanResponder: PanResponder | undefined;
 | 
					  seekPanResponder: PanResponder | undefined;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  popupInfo = () => {
 | 
				
			||||||
 | 
					    VideoDecoderProperties.getWidevineLevel().then((widevineLevel: number) => {
 | 
				
			||||||
 | 
					      VideoDecoderProperties.isHEVCSupported().then((hevcSupported: boolean) => {
 | 
				
			||||||
 | 
					        VideoDecoderProperties.isCodecSupported('video/avc', 1920, 1080).then(
 | 
				
			||||||
 | 
					          (avcSupported: boolean) => {
 | 
				
			||||||
 | 
					            this.toast(
 | 
				
			||||||
 | 
					              true,
 | 
				
			||||||
 | 
					              'Widevine level: ' +
 | 
				
			||||||
 | 
					              widevineLevel +
 | 
				
			||||||
 | 
					              '\n hevc: ' +
 | 
				
			||||||
 | 
					              (hevcSupported ? '' : 'NOT') +
 | 
				
			||||||
 | 
					              'supported' +
 | 
				
			||||||
 | 
					              '\n avc: ' +
 | 
				
			||||||
 | 
					              (avcSupported ? '' : 'NOT') +
 | 
				
			||||||
 | 
					              'supported',
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  onLoad = (data: any) => {
 | 
					  onLoad = (data: any) => {
 | 
				
			||||||
    this.setState({ duration: data.duration, loading: false, });
 | 
					    this.setState({ duration: data.duration, loading: false, });
 | 
				
			||||||
    this.onAudioTracks(data)
 | 
					    this.onAudioTracks(data)
 | 
				
			||||||
@@ -288,6 +310,18 @@ class VideoPlayer extends Component {
 | 
				
			|||||||
    )
 | 
					    )
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  renderInfoControl() {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <TouchableOpacity
 | 
				
			||||||
 | 
					        onPress={() => {
 | 
				
			||||||
 | 
					          this.popupInfo()
 | 
				
			||||||
 | 
					        }}
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					        <Text style={[styles.controlOption]}>{'decoderInfo'}</Text>
 | 
				
			||||||
 | 
					      </TouchableOpacity>
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  renderFullScreenControl() {
 | 
					  renderFullScreenControl() {
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
      <TouchableOpacity
 | 
					      <TouchableOpacity
 | 
				
			||||||
@@ -541,6 +575,9 @@ class VideoPlayer extends Component {
 | 
				
			|||||||
        </View>
 | 
					        </View>
 | 
				
			||||||
        <View style={styles.bottomControls}>
 | 
					        <View style={styles.bottomControls}>
 | 
				
			||||||
          <View style={styles.generalControls}>
 | 
					          <View style={styles.generalControls}>
 | 
				
			||||||
 | 
					            <View style={styles.generalControls}>
 | 
				
			||||||
 | 
					              <View style={styles.resizeModeControl}>{this.renderInfoControl()}</View>
 | 
				
			||||||
 | 
					            </View>
 | 
				
			||||||
            <View style={styles.resizeModeControl}>{this.renderPause()}</View>
 | 
					            <View style={styles.resizeModeControl}>{this.renderPause()}</View>
 | 
				
			||||||
            <View style={styles.resizeModeControl}>
 | 
					            <View style={styles.resizeModeControl}>
 | 
				
			||||||
              {this.renderRepeatModeControl()}
 | 
					              {this.renderRepeatModeControl()}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,6 @@
 | 
				
			|||||||
import AVFoundation
 | 
					import AVFoundation
 | 
				
			||||||
import Promises
 | 
					import Promises
 | 
				
			||||||
 | 
					import Photos
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*!
 | 
					/*!
 | 
				
			||||||
 * Collection of pure functions
 | 
					 * Collection of pure functions
 | 
				
			||||||
@@ -264,8 +265,23 @@ enum RCTVideoUtils {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
 | 
					    static func preparePHAsset(uri: String) -> Promise<AVAsset?> {
 | 
				
			||||||
 | 
					        return Promise<AVAsset?>(on: .global()) { fulfill, reject in
 | 
				
			||||||
 | 
					            let assetId = String(uri[uri.index(uri.startIndex, offsetBy: "ph://".count)...])
 | 
				
			||||||
 | 
					            guard let phAsset = PHAsset.fetchAssets(withLocalIdentifiers: [assetId], options: nil).firstObject else {
 | 
				
			||||||
 | 
					                reject(NSError(domain: "", code: 0, userInfo: nil))
 | 
				
			||||||
 | 
					                return
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            let options = PHVideoRequestOptions()
 | 
				
			||||||
 | 
					            options.isNetworkAccessAllowed = true
 | 
				
			||||||
 | 
					            PHCachingImageManager().requestAVAsset(forVideo: phAsset, options: options) { data, _, _ in
 | 
				
			||||||
 | 
					                fulfill(data)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
    static func prepareAsset(source:VideoSource) -> (asset:AVURLAsset?, assetOptions:NSMutableDictionary?)? {
 | 
					    static func prepareAsset(source:VideoSource) -> (asset:AVURLAsset?, assetOptions:NSMutableDictionary?)? {
 | 
				
			||||||
        guard source.uri != nil && source.uri != "" else { return nil }
 | 
					        guard let sourceUri = source.uri, sourceUri != "" else { return nil }
 | 
				
			||||||
        var asset:AVURLAsset!
 | 
					        var asset:AVURLAsset!
 | 
				
			||||||
        let bundlePath = Bundle.main.path(forResource: source.uri, ofType: source.type) ?? ""
 | 
					        let bundlePath = Bundle.main.path(forResource: source.uri, ofType: source.type) ?? ""
 | 
				
			||||||
        let url = source.isNetwork || source.isAsset
 | 
					        let url = source.isNetwork || source.isAsset
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -227,10 +227,20 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
 | 
				
			|||||||
        RCTVideoUtils.delay()
 | 
					        RCTVideoUtils.delay()
 | 
				
			||||||
            .then{ [weak self] in
 | 
					            .then{ [weak self] in
 | 
				
			||||||
                guard let self = self else {throw NSError(domain: "", code: 0, userInfo: nil)}
 | 
					                guard let self = self else {throw NSError(domain: "", code: 0, userInfo: nil)}
 | 
				
			||||||
                guard let source = self._source,
 | 
					                guard let source = self._source else {
 | 
				
			||||||
                let assetResult = RCTVideoUtils.prepareAsset(source: source),
 | 
					                    DebugLog("The source not exist")
 | 
				
			||||||
                let asset = assetResult.asset,
 | 
					                    throw NSError(domain: "", code: 0, userInfo: nil)
 | 
				
			||||||
                let assetOptions = assetResult.assetOptions else {
 | 
					                }
 | 
				
			||||||
 | 
					                if let uri = source.uri, uri.starts(with: "ph://") {
 | 
				
			||||||
 | 
					                    return Promise {
 | 
				
			||||||
 | 
					                        RCTVideoUtils.preparePHAsset(uri: uri).then { asset in
 | 
				
			||||||
 | 
					                            return self.playerItemPrepareText(asset:asset, assetOptions:nil)
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                guard let assetResult = RCTVideoUtils.prepareAsset(source: source),
 | 
				
			||||||
 | 
					                      let asset = assetResult.asset,
 | 
				
			||||||
 | 
					                      let assetOptions = assetResult.assetOptions else {
 | 
				
			||||||
                      DebugLog("Could not find video URL in source '\(self._source)'")
 | 
					                      DebugLog("Could not find video URL in source '\(self._source)'")
 | 
				
			||||||
                      throw NSError(domain: "", code: 0, userInfo: nil)
 | 
					                      throw NSError(domain: "", code: 0, userInfo: nil)
 | 
				
			||||||
                  }
 | 
					                  }
 | 
				
			||||||
@@ -264,7 +274,10 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
 | 
				
			|||||||
                    self._playerItem?.preferredPeakBitRate = Double(maxBitRate)
 | 
					                    self._playerItem?.preferredPeakBitRate = Double(maxBitRate)
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                
 | 
					                
 | 
				
			||||||
                self._player = AVPlayer(playerItem: self._playerItem)
 | 
					                self._player = AVPlayer()
 | 
				
			||||||
 | 
					                DispatchQueue.global(qos: .default).async {
 | 
				
			||||||
 | 
					                    self._player?.replaceCurrentItem(with: playerItem)
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
                self._playerObserver.player = self._player
 | 
					                self._playerObserver.player = self._player
 | 
				
			||||||
                self.applyModifiers()
 | 
					                self.applyModifiers()
 | 
				
			||||||
                self._player?.actionAtItemEnd = .none
 | 
					                self._player?.actionAtItemEnd = .none
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,8 +19,11 @@ class RCTVideoPlayerViewController: AVPlayerViewController {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    override func viewDidDisappear(_ animated: Bool) {
 | 
					    override func viewDidDisappear(_ animated: Bool) {
 | 
				
			||||||
        super.viewDidDisappear(animated)
 | 
					        super.viewDidDisappear(animated)
 | 
				
			||||||
        rctDelegate.videoPlayerViewControllerWillDismiss(playerViewController: self)
 | 
					
 | 
				
			||||||
        rctDelegate.videoPlayerViewControllerDidDismiss(playerViewController: self)
 | 
					        if rctDelegate != nil {
 | 
				
			||||||
 | 
					            rctDelegate.videoPlayerViewControllerWillDismiss(playerViewController: self)
 | 
				
			||||||
 | 
					            rctDelegate.videoPlayerViewControllerDidDismiss(playerViewController: self)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #if !TARGET_OS_TV
 | 
					    #if !TARGET_OS_TV
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user