commit
0583f4a0af
66
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
66
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
---
|
||||||
|
name: Bug report
|
||||||
|
about: Create a report to help us improve
|
||||||
|
title: ''
|
||||||
|
labels: ''
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Bug
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Please provide a clear and concise description of what the bug is.
|
||||||
|
Include screenshots if needed.
|
||||||
|
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)
|
||||||
|
-->
|
||||||
|
|
||||||
|
## Platform
|
||||||
|
<!--
|
||||||
|
Platform where your bug is happening. If Android, report if using Android or Android Exoplayer
|
||||||
|
-->
|
||||||
|
Which player are you experiencing the problem on:
|
||||||
|
* iOS
|
||||||
|
* Android ExoPlayer
|
||||||
|
* Android MediaPlayer
|
||||||
|
* Windows UWP
|
||||||
|
* Windows WPF
|
||||||
|
|
||||||
|
## Environment info
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Run `react-native info` in your terminal and copy the results here. Also, include the *precise* version number of this library that you are using in the project
|
||||||
|
-->
|
||||||
|
|
||||||
|
React native info output:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
// paste it here
|
||||||
|
```
|
||||||
|
|
||||||
|
Library version: x.x.x
|
||||||
|
|
||||||
|
## Steps To Reproduce
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Issues without reproduction steps or code are likely to stall.
|
||||||
|
-->
|
||||||
|
|
||||||
|
1.
|
||||||
|
2.
|
||||||
|
...
|
||||||
|
|
||||||
|
## Expected behaviour
|
||||||
|
|
||||||
|
1.
|
||||||
|
2.
|
||||||
|
|
||||||
|
## Reproducible sample code
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Please add to your issue a repro, a fresh codebase with the minimal changes so that the bug can be tested in isolation
|
||||||
|
-->
|
||||||
|
|
||||||
|
## Video sample
|
||||||
|
If possible, include a link to the video that has the problem that can be streamed or downloaded from.
|
32
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
32
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
---
|
||||||
|
name: Feature request
|
||||||
|
about: Suggest an idea for this project
|
||||||
|
title: ''
|
||||||
|
labels: ''
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Feature Request
|
||||||
|
|
||||||
|
<!--
|
||||||
|
This issue should serve for you to present or pitch an idea to the maintainers - but remember that it would be better if you were to submit a PR instead 🤗
|
||||||
|
-->
|
||||||
|
|
||||||
|
## Why it is needed
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Please tell us a bit more of why you want this feature to be added, what's its origin
|
||||||
|
-->
|
||||||
|
|
||||||
|
## Possible implementation
|
||||||
|
|
||||||
|
<!--
|
||||||
|
It really helps if you could describe from a technical POV how this new feature would work, which code it rely on, etc
|
||||||
|
-->
|
||||||
|
|
||||||
|
### Code sample
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Please show how the new code could work, if doable
|
||||||
|
-->
|
33
CHANGELOG.md
33
CHANGELOG.md
@ -1,5 +1,38 @@
|
|||||||
## Changelog
|
## Changelog
|
||||||
|
|
||||||
|
### Version 5.1.0-alpha1
|
||||||
|
* Fixed Exoplayer doesn't work with mute=true (Android). [#1696](https://github.com/react-native-community/react-native-video/pull/1696)
|
||||||
|
* Added support for automaticallyWaitsToMinimizeStalling property (iOS) [#1723](https://github.com/react-native-community/react-native-video/pull/1723)
|
||||||
|
* Bump Exoplayer to 2.10.4, remove deprecated usages of Exoplayer methods (Android). [#1753](https://github.com/react-native-community/react-native-video/pull/1753)
|
||||||
|
* Preserve Exoplayer BandwidthMeter instance across video plays, this should noticeably improve streaming bandwidth detection (Android).
|
||||||
|
|
||||||
|
### Version 5.0.2
|
||||||
|
* Fix crash when RCTVideo's superclass doesn't observe the keyPath 'frame' (iOS) [#1720](https://github.com/react-native-community/react-native-video/pull/1720)
|
||||||
|
|
||||||
|
### Version 5.0.1
|
||||||
|
* Fix AndroidX Support bad merge
|
||||||
|
|
||||||
|
### Version 5.0.0 [Deprecated]
|
||||||
|
* AndroidX Support
|
||||||
|
|
||||||
|
### Version 4.4.4
|
||||||
|
* Handle racing conditions when props are setted on exoplayer
|
||||||
|
|
||||||
|
### Version 4.4.3
|
||||||
|
* Fix mute/unmute when controls are present (iOS) [#1654](https://github.com/react-native-community/react-native-video/pull/1654)
|
||||||
|
* Fix Android videos being able to play with background music/audio from other apps.
|
||||||
|
* Fixed memory leak on iOS when using `controls` [#1647](https://github.com/react-native-community/react-native-video/pull/1647)
|
||||||
|
* (Android) Update gradle and target SDK [#1629](https://github.com/react-native-community/react-native-video/pull/1629)
|
||||||
|
* Fix iOS stressed mount/unmount crash [#1646](https://github.com/react-native-community/react-native-video/pull/1646)
|
||||||
|
|
||||||
|
### Version 4.4.2
|
||||||
|
* Change compileOnly to implementation on gradle (for newer gradle versions and react-native 0.59 support) [#1592](https://github.com/react-native-community/react-native-video/pull/1592)
|
||||||
|
* Replaced RCTBubblingEventBlock events by RCTDirectEventBlock to avoid event name collisions [#1625](https://github.com/react-native-community/react-native-video/pull/1625)
|
||||||
|
* Added `onPlaybackRateChange` to README [#1578](https://github.com/react-native-community/react-native-video/pull/1578)
|
||||||
|
* Added `onReadyForDisplay` to README [#1627](https://github.com/react-native-community/react-native-video/pull/1627)
|
||||||
|
* Improved handling of poster image. Fixes bug with displaying video and poster simultaneously. [#1627](https://github.com/react-native-community/react-native-video/pull/1627)
|
||||||
|
* Fix background audio stopping on iOS when using `controls` [#1614](https://github.com/react-native-community/react-native-video/pull/1614)
|
||||||
|
|
||||||
### Version 4.4.1
|
### Version 4.4.1
|
||||||
* Fix tvOS picture-in-picture compilation regression [#1518](https://github.com/react-native-community/react-native-video/pull/1518)
|
* Fix tvOS picture-in-picture compilation regression [#1518](https://github.com/react-native-community/react-native-video/pull/1518)
|
||||||
* fullscreen rotation issues with iOS built-in controls [#1441](https://github.com/react-native-community/react-native-video/pull/1441)
|
* fullscreen rotation issues with iOS built-in controls [#1441](https://github.com/react-native-community/react-native-video/pull/1441)
|
||||||
|
145
README.md
145
README.md
@ -3,10 +3,16 @@
|
|||||||
A `<Video>` component for react-native, as seen in
|
A `<Video>` component for react-native, as seen in
|
||||||
[react-native-login](https://github.com/brentvatne/react-native-login)!
|
[react-native-login](https://github.com/brentvatne/react-native-login)!
|
||||||
|
|
||||||
|
Version 5.x recommends react-native >= 0.60.0 for Android 64bit builds and Android X support.
|
||||||
|
|
||||||
Version 4.x requires react-native >= 0.57.0
|
Version 4.x requires react-native >= 0.57.0
|
||||||
|
|
||||||
Version 3.x requires react-native >= 0.40.0
|
Version 3.x requires react-native >= 0.40.0
|
||||||
|
|
||||||
|
### Version 5.0.0 breaking changes
|
||||||
|
|
||||||
|
Version 5 introduces breaking changes on Android, please check carefully the steps described there: [Android Installation](#Android-installation)
|
||||||
|
|
||||||
### Version 4.0.0 breaking changes
|
### Version 4.0.0 breaking changes
|
||||||
Version 4.0.0 changes some behaviors and may require updates to your Gradle files. See [Updating](#updating) for details.
|
Version 4.0.0 changes some behaviors and may require updates to your Gradle files. See [Updating](#updating) for details.
|
||||||
|
|
||||||
@ -20,6 +26,11 @@ Version 3.0 features a number of changes to existing behavior. See [Updating](#u
|
|||||||
## Table of Contents
|
## Table of Contents
|
||||||
|
|
||||||
* [Installation](#installation)
|
* [Installation](#installation)
|
||||||
|
* [iOS](#ios-installation)
|
||||||
|
* [tvOS](#tvos-installation)
|
||||||
|
* [Android](#android-installation)
|
||||||
|
* [Windows](#windows-installation)
|
||||||
|
* [react-native-dom](#react-native-dom-installation)
|
||||||
* [Usage](#usage)
|
* [Usage](#usage)
|
||||||
* [iOS App Transport Security](#ios-app-transport-security)
|
* [iOS App Transport Security](#ios-app-transport-security)
|
||||||
* [Audio Mixing](#audio-mixing)
|
* [Audio Mixing](#audio-mixing)
|
||||||
@ -42,14 +53,21 @@ yarn add react-native-video
|
|||||||
|
|
||||||
Then follow the instructions for your platform to link react-native-video into your project:
|
Then follow the instructions for your platform to link react-native-video into your project:
|
||||||
|
|
||||||
|
### iOS installation
|
||||||
<details>
|
<details>
|
||||||
<summary>iOS</summary>
|
<summary>iOS details</summary>
|
||||||
|
|
||||||
### Standard Method
|
#### Standard Method
|
||||||
|
|
||||||
|
**React Native 0.60 and above**
|
||||||
|
|
||||||
|
Run `pod install` in the `ios` directory. Linking is not required in React Native 0.60 and above.
|
||||||
|
|
||||||
|
**React Native 0.59 and below**
|
||||||
|
|
||||||
Run `react-native link react-native-video` to link the react-native-video library.
|
Run `react-native link react-native-video` to link the react-native-video library.
|
||||||
|
|
||||||
### Using CocoaPods (required to enable caching)
|
#### Using CocoaPods (required to enable caching)
|
||||||
|
|
||||||
Setup your Podfile like it is described in the [react-native documentation](https://facebook.github.io/react-native/docs/integration-with-existing-apps#configuring-cocoapods-dependencies).
|
Setup your Podfile like it is described in the [react-native documentation](https://facebook.github.io/react-native/docs/integration-with-existing-apps#configuring-cocoapods-dependencies).
|
||||||
|
|
||||||
@ -73,8 +91,9 @@ end
|
|||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
<details>
|
### tvOS installation
|
||||||
<summary>tvOS</summary>
|
<details>
|
||||||
|
<summary>tvOS details</summary>
|
||||||
|
|
||||||
`react-native link react-native-video` doesn’t work properly with the tvOS target so we need to add the library manually.
|
`react-native link react-native-video` doesn’t work properly with the tvOS target so we need to add the library manually.
|
||||||
|
|
||||||
@ -95,14 +114,15 @@ Select RCTVideo-tvOS
|
|||||||
<img src="./docs/tvOS-step-4.jpg" width="40%">
|
<img src="./docs/tvOS-step-4.jpg" width="40%">
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
### Android installation
|
||||||
<details>
|
<details>
|
||||||
<summary>Android</summary>
|
<summary>Android details</summary>
|
||||||
|
|
||||||
Run `react-native link react-native-video` to link the react-native-video library.
|
Run `react-native link react-native-video` to link the react-native-video library.
|
||||||
|
|
||||||
Or if you have trouble, make the following additions to the given files manually:
|
Or if you have trouble, make the following additions to the given files manually:
|
||||||
|
|
||||||
**android/settings.gradle**
|
#### **android/settings.gradle**
|
||||||
|
|
||||||
The newer ExoPlayer library will work for most people.
|
The newer ExoPlayer library will work for most people.
|
||||||
|
|
||||||
@ -118,17 +138,30 @@ include ':react-native-video'
|
|||||||
project(':react-native-video').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-video/android')
|
project(':react-native-video').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-video/android')
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### **android/app/build.gradle**
|
||||||
|
|
||||||
**android/app/build.gradle**
|
From version >= 5.0.0, you have to apply this changes:
|
||||||
|
|
||||||
```gradle
|
```diff
|
||||||
dependencies {
|
dependencies {
|
||||||
...
|
...
|
||||||
compile project(':react-native-video')
|
compile project(':react-native-video')
|
||||||
|
+ implementation "androidx.appcompat:appcompat:1.0.0"
|
||||||
|
- implementation "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}"
|
||||||
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
**MainApplication.java**
|
#### **android/gradle.properties**
|
||||||
|
|
||||||
|
Migrating to AndroidX (needs version >= 5.0.0):
|
||||||
|
|
||||||
|
```gradle.properties
|
||||||
|
android.useAndroidX=true
|
||||||
|
android.enableJetifier=true
|
||||||
|
```
|
||||||
|
|
||||||
|
#### **MainApplication.java**
|
||||||
|
|
||||||
On top, where imports are:
|
On top, where imports are:
|
||||||
|
|
||||||
@ -149,12 +182,13 @@ protected List<ReactPackage> getPackages() {
|
|||||||
```
|
```
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
### Windows installation
|
||||||
<details>
|
<details>
|
||||||
<summary>Windows</summary>
|
<summary>Windows details</summary>
|
||||||
|
|
||||||
Make the following additions to the given files manually:
|
Make the following additions to the given files manually:
|
||||||
|
|
||||||
**windows/myapp.sln**
|
#### **windows/myapp.sln**
|
||||||
|
|
||||||
Add the `ReactNativeVideo` project to your solution.
|
Add the `ReactNativeVideo` project to your solution.
|
||||||
|
|
||||||
@ -163,7 +197,7 @@ Add the `ReactNativeVideo` project to your solution.
|
|||||||
* UWP: Select `node_modules\react-native-video\windows\ReactNativeVideo\ReactNativeVideo.csproj`
|
* UWP: Select `node_modules\react-native-video\windows\ReactNativeVideo\ReactNativeVideo.csproj`
|
||||||
* WPF: Select `node_modules\react-native-video\windows\ReactNativeVideo.Net46\ReactNativeVideo.Net46.csproj`
|
* WPF: Select `node_modules\react-native-video\windows\ReactNativeVideo.Net46\ReactNativeVideo.Net46.csproj`
|
||||||
|
|
||||||
**windows/myapp/myapp.csproj**
|
#### **windows/myapp/myapp.csproj**
|
||||||
|
|
||||||
Add a reference to `ReactNativeVideo` to your main application project. From Visual Studio 2015:
|
Add a reference to `ReactNativeVideo` to your main application project. From Visual Studio 2015:
|
||||||
|
|
||||||
@ -171,7 +205,7 @@ Add a reference to `ReactNativeVideo` to your main application project. From Vis
|
|||||||
* UWP: Check `ReactNativeVideo` from Solution Projects.
|
* UWP: Check `ReactNativeVideo` from Solution Projects.
|
||||||
* WPF: Check `ReactNativeVideo.Net46` from Solution Projects.
|
* WPF: Check `ReactNativeVideo.Net46` from Solution Projects.
|
||||||
|
|
||||||
**MainPage.cs**
|
#### **MainPage.cs**
|
||||||
|
|
||||||
Add the `ReactVideoPackage` class to your list of exported packages.
|
Add the `ReactVideoPackage` class to your list of exported packages.
|
||||||
```cs
|
```cs
|
||||||
@ -198,12 +232,13 @@ using System.Collections.Generic;
|
|||||||
```
|
```
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
### react-native-dom installation
|
||||||
<details>
|
<details>
|
||||||
<summary>react-native-dom</summary>
|
<summary>react-native-dom details</summary>
|
||||||
|
|
||||||
Make the following additions to the given files manually:
|
Make the following additions to the given files manually:
|
||||||
|
|
||||||
**dom/bootstrap.js**
|
#### **dom/bootstrap.js**
|
||||||
|
|
||||||
Import RCTVideoManager and add it to the list of nativeModules:
|
Import RCTVideoManager and add it to the list of nativeModules:
|
||||||
|
|
||||||
@ -257,8 +292,10 @@ var styles = StyleSheet.create({
|
|||||||
### Configurable props
|
### Configurable props
|
||||||
* [allowsExternalPlayback](#allowsexternalplayback)
|
* [allowsExternalPlayback](#allowsexternalplayback)
|
||||||
* [audioOnly](#audioonly)
|
* [audioOnly](#audioonly)
|
||||||
|
* [automaticallyWaitsToMinimizeStalling](#automaticallyWaitsToMinimizeStalling)
|
||||||
* [bufferConfig](#bufferconfig)
|
* [bufferConfig](#bufferconfig)
|
||||||
* [controls](#controls)
|
* [controls](#controls)
|
||||||
|
* [disableFocus](#disableFocus)
|
||||||
* [filter](#filter)
|
* [filter](#filter)
|
||||||
* [filterEnabled](#filterEnabled)
|
* [filterEnabled](#filterEnabled)
|
||||||
* [fullscreen](#fullscreen)
|
* [fullscreen](#fullscreen)
|
||||||
@ -302,7 +339,9 @@ var styles = StyleSheet.create({
|
|||||||
* [onFullscreenPlayerDidDismiss](#onfullscreenplayerdiddismiss)
|
* [onFullscreenPlayerDidDismiss](#onfullscreenplayerdiddismiss)
|
||||||
* [onLoad](#onload)
|
* [onLoad](#onload)
|
||||||
* [onLoadStart](#onloadstart)
|
* [onLoadStart](#onloadstart)
|
||||||
|
* [onReadyForDisplay](#onreadyfordisplay)
|
||||||
* [onPictureInPictureStatusChanged](#onpictureinpicturestatuschanged)
|
* [onPictureInPictureStatusChanged](#onpictureinpicturestatuschanged)
|
||||||
|
* [onPlaybackRateChange](#onplaybackratechange)
|
||||||
* [onProgress](#onprogress)
|
* [onProgress](#onprogress)
|
||||||
* [onSeek](#onseek)
|
* [onSeek](#onseek)
|
||||||
* [onRestoreUserInterfaceForPictureInPictureStop](#onrestoreuserinterfaceforpictureinpicturestop)
|
* [onRestoreUserInterfaceForPictureInPictureStop](#onrestoreuserinterfaceforpictureinpicturestop)
|
||||||
@ -333,6 +372,13 @@ For this to work, the poster prop must be set.
|
|||||||
|
|
||||||
Platforms: all
|
Platforms: all
|
||||||
|
|
||||||
|
#### automaticallyWaitsToMinimizeStalling
|
||||||
|
A Boolean value that indicates whether the player should automatically delay playback in order to minimize stalling. For clients linked against iOS 10.0 and later
|
||||||
|
* **false** - Immediately starts playback
|
||||||
|
* **true (default)** - Delays playback in order to minimize stalling
|
||||||
|
|
||||||
|
Platforms: iOS
|
||||||
|
|
||||||
#### bufferConfig
|
#### bufferConfig
|
||||||
Adjust the buffer settings. This prop takes an object with one or more of the properties listed below.
|
Adjust the buffer settings. This prop takes an object with one or more of the properties listed below.
|
||||||
|
|
||||||
@ -368,6 +414,13 @@ For Android MediaPlayer, you will need to build your own controls or use a packa
|
|||||||
|
|
||||||
Platforms: Android ExoPlayer, iOS, react-native-dom
|
Platforms: Android ExoPlayer, iOS, react-native-dom
|
||||||
|
|
||||||
|
#### disableFocus
|
||||||
|
Determines whether video audio should override background music/audio in Android devices.
|
||||||
|
* ** false (default)** - Override background audio/music
|
||||||
|
* **true** - Let background audio/music from other apps play
|
||||||
|
|
||||||
|
Platforms: Android Exoplayer
|
||||||
|
|
||||||
#### filter
|
#### filter
|
||||||
Add video filter
|
Add video filter
|
||||||
* **FilterType.NONE (default)** - No Filter
|
* **FilterType.NONE (default)** - No Filter
|
||||||
@ -953,6 +1006,17 @@ Example:
|
|||||||
|
|
||||||
Platforms: all
|
Platforms: all
|
||||||
|
|
||||||
|
#### onReadyForDisplay
|
||||||
|
Callback function that is called when the first video frame is ready for display. This is when the poster is removed.
|
||||||
|
|
||||||
|
Payload: none
|
||||||
|
|
||||||
|
* iOS: [readyForDisplay](https://developer.apple.com/documentation/avkit/avplayerviewcontroller/1615830-readyfordisplay?language=objc)
|
||||||
|
* Android: [MEDIA_INFO_VIDEO_RENDERING_START](https://developer.android.com/reference/android/media/MediaPlayer#MEDIA_INFO_VIDEO_RENDERING_START)
|
||||||
|
* Android ExoPlayer [STATE_READY](https://exoplayer.dev/doc/reference/com/google/android/exoplayer2/Player.html#STATE_READY)
|
||||||
|
|
||||||
|
Platforms: Android ExoPlayer, Android MediaPlayer, iOS, Web
|
||||||
|
|
||||||
#### onPictureInPictureStatusChanged
|
#### onPictureInPictureStatusChanged
|
||||||
Callback function that is called when picture in picture becomes active or inactive.
|
Callback function that is called when picture in picture becomes active or inactive.
|
||||||
|
|
||||||
@ -969,6 +1033,23 @@ isActive: true
|
|||||||
|
|
||||||
Platforms: iOS
|
Platforms: iOS
|
||||||
|
|
||||||
|
#### onPlaybackRateChange
|
||||||
|
Callback function that is called when the rate of playback changes - either paused or starts/resumes.
|
||||||
|
|
||||||
|
Property | Type | Description
|
||||||
|
--- | --- | ---
|
||||||
|
playbackRate | number | 0 when playback is paused, 1 when playing at normal speed. Other values when playback is slowed down or sped up
|
||||||
|
|
||||||
|
Example:
|
||||||
|
```
|
||||||
|
{
|
||||||
|
playbackRate: 0, // indicates paused
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Platforms: all
|
||||||
|
|
||||||
|
|
||||||
#### onProgress
|
#### onProgress
|
||||||
Callback function that is called every progressUpdateInterval seconds with info about which position the media is currently playing.
|
Callback function that is called every progressUpdateInterval seconds with info about which position the media is currently playing.
|
||||||
|
|
||||||
@ -1231,6 +1312,38 @@ To enable audio to play in background on iOS the audio session needs to be set t
|
|||||||
|
|
||||||
## Updating
|
## Updating
|
||||||
|
|
||||||
|
### Version 5.0.0
|
||||||
|
|
||||||
|
Probably you want to update your gradle version:
|
||||||
|
#### gradle-wrapper.properties
|
||||||
|
```diff
|
||||||
|
- distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
|
||||||
|
+ distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
|
||||||
|
```
|
||||||
|
|
||||||
|
#### **android/app/build.gradle**
|
||||||
|
|
||||||
|
From version >= 5.0.0, you have to apply this changes:
|
||||||
|
|
||||||
|
```diff
|
||||||
|
dependencies {
|
||||||
|
...
|
||||||
|
compile project(':react-native-video')
|
||||||
|
+ implementation "androidx.appcompat:appcompat:1.0.0"
|
||||||
|
- implementation "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}"
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### **android/gradle.properties**
|
||||||
|
|
||||||
|
Migrating to AndroidX (needs version >= 5.0.0):
|
||||||
|
|
||||||
|
```gradle.properties
|
||||||
|
android.useAndroidX=true
|
||||||
|
android.enableJetifier=true
|
||||||
|
```
|
||||||
|
|
||||||
### Version 4.0.0
|
### Version 4.0.0
|
||||||
|
|
||||||
#### Gradle 3 and target SDK 26 requirement
|
#### Gradle 3 and target SDK 26 requirement
|
||||||
|
35
Video.js
35
Video.js
@ -20,7 +20,7 @@ export default class Video extends Component {
|
|||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
showPoster: true,
|
showPoster: !!props.poster
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,6 +86,12 @@ export default class Video extends Component {
|
|||||||
this._root = component;
|
this._root = component;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
_hidePoster = () => {
|
||||||
|
if (this.state.showPoster) {
|
||||||
|
this.setState({showPoster: false});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_onLoadStart = (event) => {
|
_onLoadStart = (event) => {
|
||||||
if (this.props.onLoadStart) {
|
if (this.props.onLoadStart) {
|
||||||
this.props.onLoadStart(event.nativeEvent);
|
this.props.onLoadStart(event.nativeEvent);
|
||||||
@ -93,6 +99,10 @@ export default class Video extends Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
_onLoad = (event) => {
|
_onLoad = (event) => {
|
||||||
|
// Need to hide poster here for windows as onReadyForDisplay is not implemented
|
||||||
|
if (Platform.OS === 'windows') {
|
||||||
|
this._hidePoster();
|
||||||
|
}
|
||||||
if (this.props.onLoad) {
|
if (this.props.onLoad) {
|
||||||
this.props.onLoad(event.nativeEvent);
|
this.props.onLoad(event.nativeEvent);
|
||||||
}
|
}
|
||||||
@ -117,10 +127,6 @@ export default class Video extends Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
_onSeek = (event) => {
|
_onSeek = (event) => {
|
||||||
if (this.state.showPoster && !this.props.audioOnly) {
|
|
||||||
this.setState({showPoster: false});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.props.onSeek) {
|
if (this.props.onSeek) {
|
||||||
this.props.onSeek(event.nativeEvent);
|
this.props.onSeek(event.nativeEvent);
|
||||||
}
|
}
|
||||||
@ -163,6 +169,7 @@ export default class Video extends Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
_onReadyForDisplay = (event) => {
|
_onReadyForDisplay = (event) => {
|
||||||
|
this._hidePoster();
|
||||||
if (this.props.onReadyForDisplay) {
|
if (this.props.onReadyForDisplay) {
|
||||||
this.props.onReadyForDisplay(event.nativeEvent);
|
this.props.onReadyForDisplay(event.nativeEvent);
|
||||||
}
|
}
|
||||||
@ -181,10 +188,6 @@ export default class Video extends Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
_onPlaybackRateChange = (event) => {
|
_onPlaybackRateChange = (event) => {
|
||||||
if (this.state.showPoster && event.nativeEvent.playbackRate !== 0 && !this.props.audioOnly) {
|
|
||||||
this.setState({showPoster: false});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.props.onPlaybackRateChange) {
|
if (this.props.onPlaybackRateChange) {
|
||||||
this.props.onPlaybackRateChange(event.nativeEvent);
|
this.props.onPlaybackRateChange(event.nativeEvent);
|
||||||
}
|
}
|
||||||
@ -308,15 +311,16 @@ export default class Video extends Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
|
||||||
<RCTVideo ref={this._assignRoot} {...nativeProps} />
|
|
||||||
{this.props.poster &&
|
|
||||||
this.state.showPoster && (
|
|
||||||
<View style={nativeProps.style}>
|
<View style={nativeProps.style}>
|
||||||
|
<RCTVideo
|
||||||
|
ref={this._assignRoot}
|
||||||
|
{...nativeProps}
|
||||||
|
style={StyleSheet.absoluteFill}
|
||||||
|
/>
|
||||||
|
{this.state.showPoster && (
|
||||||
<Image style={posterStyle} source={{ uri: this.props.poster }} />
|
<Image style={posterStyle} source={{ uri: this.props.poster }} />
|
||||||
</View>
|
|
||||||
)}
|
)}
|
||||||
</React.Fragment>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -378,6 +382,7 @@ Video.propTypes = {
|
|||||||
poster: PropTypes.string,
|
poster: PropTypes.string,
|
||||||
posterResizeMode: Image.propTypes.resizeMode,
|
posterResizeMode: Image.propTypes.resizeMode,
|
||||||
repeat: PropTypes.bool,
|
repeat: PropTypes.bool,
|
||||||
|
automaticallyWaitsToMinimizeStalling: PropTypes.bool,
|
||||||
allowsExternalPlayback: PropTypes.bool,
|
allowsExternalPlayback: PropTypes.bool,
|
||||||
selectedAudioTrack: PropTypes.shape({
|
selectedAudioTrack: PropTypes.shape({
|
||||||
type: PropTypes.string.isRequired,
|
type: PropTypes.string.isRequired,
|
||||||
|
@ -5,8 +5,8 @@ def safeExtGet(prop, fallback) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion safeExtGet('compileSdkVersion', 27)
|
compileSdkVersion safeExtGet('compileSdkVersion', 28)
|
||||||
buildToolsVersion safeExtGet('buildToolsVersion', '27.0.3')
|
buildToolsVersion safeExtGet('buildToolsVersion', '28.0.3')
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
targetCompatibility JavaVersion.VERSION_1_8
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
@ -15,26 +15,26 @@ android {
|
|||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion safeExtGet('minSdkVersion', 16)
|
minSdkVersion safeExtGet('minSdkVersion', 16)
|
||||||
targetSdkVersion safeExtGet('targetSdkVersion', 27)
|
targetSdkVersion safeExtGet('targetSdkVersion', 28)
|
||||||
versionCode 1
|
versionCode 1
|
||||||
versionName "1.0"
|
versionName "1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compileOnly "com.facebook.react:react-native:${safeExtGet('reactNativeVersion', '+')}"
|
implementation "com.facebook.react:react-native:${safeExtGet('reactNativeVersion', '+')}"
|
||||||
implementation('com.google.android.exoplayer:exoplayer:2.9.3') {
|
implementation('com.google.android.exoplayer:exoplayer:2.10.4') {
|
||||||
exclude group: 'com.android.support'
|
exclude group: 'com.android.support'
|
||||||
}
|
}
|
||||||
|
|
||||||
// All support libs must use the same version
|
// All support libs must use the same version
|
||||||
implementation "com.android.support:support-annotations:${safeExtGet('supportLibVersion', '+')}"
|
implementation "androidx.annotation:annotation:1.1.0"
|
||||||
implementation "com.android.support:support-compat:${safeExtGet('supportLibVersion', '+')}"
|
implementation "androidx.core:core:1.1.0"
|
||||||
implementation "com.android.support:support-media-compat:${safeExtGet('supportLibVersion', '+')}"
|
implementation "androidx.media:media:1.1.0"
|
||||||
|
|
||||||
implementation('com.google.android.exoplayer:extension-okhttp:2.9.3') {
|
implementation('com.google.android.exoplayer:extension-okhttp:2.10.4') {
|
||||||
exclude group: 'com.squareup.okhttp3', module: 'okhttp'
|
exclude group: 'com.squareup.okhttp3', module: 'okhttp'
|
||||||
}
|
}
|
||||||
implementation 'com.squareup.okhttp3:okhttp:3.12.1'
|
implementation 'com.squareup.okhttp3:okhttp:3.14.3'
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
package com.brentvatne.exoplayer;
|
package com.brentvatne.exoplayer;
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.ContextWrapper;
|
|
||||||
|
|
||||||
import com.facebook.react.bridge.ReactContext;
|
import com.facebook.react.bridge.ReactContext;
|
||||||
import com.facebook.react.modules.network.CookieJarContainer;
|
import com.facebook.react.modules.network.CookieJarContainer;
|
||||||
import com.facebook.react.modules.network.ForwardingCookieHandler;
|
import com.facebook.react.modules.network.ForwardingCookieHandler;
|
||||||
@ -14,12 +11,10 @@ import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
|
|||||||
import com.google.android.exoplayer2.upstream.HttpDataSource;
|
import com.google.android.exoplayer2.upstream.HttpDataSource;
|
||||||
import com.google.android.exoplayer2.util.Util;
|
import com.google.android.exoplayer2.util.Util;
|
||||||
|
|
||||||
import okhttp3.Cookie;
|
|
||||||
import okhttp3.JavaNetCookieJar;
|
import okhttp3.JavaNetCookieJar;
|
||||||
import okhttp3.OkHttpClient;
|
import okhttp3.OkHttpClient;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
|
||||||
public class DataSourceUtil {
|
public class DataSourceUtil {
|
||||||
|
|
||||||
private DataSourceUtil() {
|
private DataSourceUtil() {
|
||||||
|
@ -0,0 +1,26 @@
|
|||||||
|
package com.brentvatne.exoplayer;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
|
||||||
|
import com.google.android.exoplayer2.upstream.DefaultLoadErrorHandlingPolicy;
|
||||||
|
import com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy;
|
||||||
|
|
||||||
|
public class DefaultReactExoplayerConfig implements ReactExoplayerConfig {
|
||||||
|
|
||||||
|
private final DefaultBandwidthMeter bandwidthMeter;
|
||||||
|
|
||||||
|
public DefaultReactExoplayerConfig(Context context) {
|
||||||
|
this.bandwidthMeter = new DefaultBandwidthMeter.Builder(context).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LoadErrorHandlingPolicy buildLoadErrorHandlingPolicy(int minLoadRetryCount) {
|
||||||
|
return new DefaultLoadErrorHandlingPolicy(minLoadRetryCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DefaultBandwidthMeter getBandwidthMeter() {
|
||||||
|
return bandwidthMeter;
|
||||||
|
}
|
||||||
|
}
|
@ -2,7 +2,7 @@ package com.brentvatne.exoplayer;
|
|||||||
|
|
||||||
import android.annotation.TargetApi;
|
import android.annotation.TargetApi;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.support.v4.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.view.Gravity;
|
import android.view.Gravity;
|
||||||
|
@ -15,6 +15,6 @@ class RawResourceDataSourceFactory implements DataSource.Factory {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DataSource createDataSource() {
|
public DataSource createDataSource() {
|
||||||
return new RawResourceDataSource(context, null);
|
return new RawResourceDataSource(context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,13 @@
|
|||||||
|
package com.brentvatne.exoplayer;
|
||||||
|
|
||||||
|
import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
|
||||||
|
import com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extension points to configure the Exoplayer instance
|
||||||
|
*/
|
||||||
|
public interface ReactExoplayerConfig {
|
||||||
|
LoadErrorHandlingPolicy buildLoadErrorHandlingPolicy(int minLoadRetryCount);
|
||||||
|
|
||||||
|
DefaultBandwidthMeter getBandwidthMeter();
|
||||||
|
}
|
@ -27,26 +27,25 @@ import com.facebook.react.bridge.WritableMap;
|
|||||||
import com.facebook.react.uimanager.ThemedReactContext;
|
import com.facebook.react.uimanager.ThemedReactContext;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
import com.google.android.exoplayer2.DefaultLoadControl;
|
import com.google.android.exoplayer2.DefaultLoadControl;
|
||||||
|
import com.google.android.exoplayer2.DefaultRenderersFactory;
|
||||||
import com.google.android.exoplayer2.ExoPlaybackException;
|
import com.google.android.exoplayer2.ExoPlaybackException;
|
||||||
import com.google.android.exoplayer2.ExoPlayer;
|
|
||||||
import com.google.android.exoplayer2.ExoPlayerFactory;
|
import com.google.android.exoplayer2.ExoPlayerFactory;
|
||||||
import com.google.android.exoplayer2.Format;
|
import com.google.android.exoplayer2.Format;
|
||||||
import com.google.android.exoplayer2.PlaybackParameters;
|
import com.google.android.exoplayer2.PlaybackParameters;
|
||||||
import com.google.android.exoplayer2.Player;
|
import com.google.android.exoplayer2.Player;
|
||||||
import com.google.android.exoplayer2.SimpleExoPlayer;
|
import com.google.android.exoplayer2.SimpleExoPlayer;
|
||||||
import com.google.android.exoplayer2.Timeline;
|
import com.google.android.exoplayer2.Timeline;
|
||||||
import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory;
|
|
||||||
import com.google.android.exoplayer2.mediacodec.MediaCodecRenderer;
|
import com.google.android.exoplayer2.mediacodec.MediaCodecRenderer;
|
||||||
import com.google.android.exoplayer2.mediacodec.MediaCodecUtil;
|
import com.google.android.exoplayer2.mediacodec.MediaCodecUtil;
|
||||||
import com.google.android.exoplayer2.metadata.Metadata;
|
import com.google.android.exoplayer2.metadata.Metadata;
|
||||||
import com.google.android.exoplayer2.metadata.MetadataRenderer;
|
import com.google.android.exoplayer2.metadata.MetadataOutput;
|
||||||
import com.google.android.exoplayer2.source.BehindLiveWindowException;
|
import com.google.android.exoplayer2.source.BehindLiveWindowException;
|
||||||
import com.google.android.exoplayer2.source.ExtractorMediaSource;
|
|
||||||
import com.google.android.exoplayer2.source.MediaSource;
|
import com.google.android.exoplayer2.source.MediaSource;
|
||||||
import com.google.android.exoplayer2.source.MergingMediaSource;
|
import com.google.android.exoplayer2.source.MergingMediaSource;
|
||||||
|
import com.google.android.exoplayer2.source.ProgressiveMediaSource;
|
||||||
import com.google.android.exoplayer2.source.SingleSampleMediaSource;
|
import com.google.android.exoplayer2.source.SingleSampleMediaSource;
|
||||||
import com.google.android.exoplayer2.source.TrackGroupArray;
|
|
||||||
import com.google.android.exoplayer2.source.TrackGroup;
|
import com.google.android.exoplayer2.source.TrackGroup;
|
||||||
|
import com.google.android.exoplayer2.source.TrackGroupArray;
|
||||||
import com.google.android.exoplayer2.source.dash.DashMediaSource;
|
import com.google.android.exoplayer2.source.dash.DashMediaSource;
|
||||||
import com.google.android.exoplayer2.source.dash.DefaultDashChunkSource;
|
import com.google.android.exoplayer2.source.dash.DefaultDashChunkSource;
|
||||||
import com.google.android.exoplayer2.source.hls.HlsMediaSource;
|
import com.google.android.exoplayer2.source.hls.HlsMediaSource;
|
||||||
@ -54,42 +53,36 @@ import com.google.android.exoplayer2.source.smoothstreaming.DefaultSsChunkSource
|
|||||||
import com.google.android.exoplayer2.source.smoothstreaming.SsMediaSource;
|
import com.google.android.exoplayer2.source.smoothstreaming.SsMediaSource;
|
||||||
import com.google.android.exoplayer2.trackselection.AdaptiveTrackSelection;
|
import com.google.android.exoplayer2.trackselection.AdaptiveTrackSelection;
|
||||||
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
|
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
|
||||||
import com.google.android.exoplayer2.trackselection.FixedTrackSelection;
|
|
||||||
import com.google.android.exoplayer2.trackselection.MappingTrackSelector;
|
import com.google.android.exoplayer2.trackselection.MappingTrackSelector;
|
||||||
import com.google.android.exoplayer2.trackselection.TrackSelection;
|
import com.google.android.exoplayer2.trackselection.TrackSelection;
|
||||||
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
|
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
|
||||||
|
import com.google.android.exoplayer2.ui.PlayerControlView;
|
||||||
|
import com.google.android.exoplayer2.upstream.BandwidthMeter;
|
||||||
import com.google.android.exoplayer2.upstream.DataSource;
|
import com.google.android.exoplayer2.upstream.DataSource;
|
||||||
import com.google.android.exoplayer2.upstream.DefaultAllocator;
|
import com.google.android.exoplayer2.upstream.DefaultAllocator;
|
||||||
import com.google.android.exoplayer2.upstream.BandwidthMeter;
|
|
||||||
import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
|
import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
|
||||||
import com.google.android.exoplayer2.util.MimeTypes;
|
|
||||||
import com.google.android.exoplayer2.util.Util;
|
import com.google.android.exoplayer2.util.Util;
|
||||||
import com.google.android.exoplayer2.ui.PlayerControlView;
|
|
||||||
|
|
||||||
import java.net.CookieHandler;
|
import java.net.CookieHandler;
|
||||||
import java.net.CookieManager;
|
import java.net.CookieManager;
|
||||||
import java.net.CookiePolicy;
|
import java.net.CookiePolicy;
|
||||||
import java.lang.Math;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.lang.Object;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
@SuppressLint("ViewConstructor")
|
@SuppressLint("ViewConstructor")
|
||||||
class ReactExoplayerView extends FrameLayout implements
|
class ReactExoplayerView extends FrameLayout implements
|
||||||
LifecycleEventListener,
|
LifecycleEventListener,
|
||||||
ExoPlayer.EventListener,
|
Player.EventListener,
|
||||||
BandwidthMeter.EventListener,
|
BandwidthMeter.EventListener,
|
||||||
BecomingNoisyListener,
|
BecomingNoisyListener,
|
||||||
AudioManager.OnAudioFocusChangeListener,
|
AudioManager.OnAudioFocusChangeListener,
|
||||||
MetadataRenderer.Output {
|
MetadataOutput {
|
||||||
|
|
||||||
private static final String TAG = "ReactExoplayerView";
|
private static final String TAG = "ReactExoplayerView";
|
||||||
|
|
||||||
private static final DefaultBandwidthMeter BANDWIDTH_METER = new DefaultBandwidthMeter();
|
|
||||||
private static final CookieManager DEFAULT_COOKIE_MANAGER;
|
private static final CookieManager DEFAULT_COOKIE_MANAGER;
|
||||||
private static final int SHOW_PROGRESS = 1;
|
private static final int SHOW_PROGRESS = 1;
|
||||||
private static final int REPORT_BANDWIDTH = 1;
|
|
||||||
|
|
||||||
static {
|
static {
|
||||||
DEFAULT_COOKIE_MANAGER = new CookieManager();
|
DEFAULT_COOKIE_MANAGER = new CookieManager();
|
||||||
@ -97,11 +90,12 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
private final VideoEventEmitter eventEmitter;
|
private final VideoEventEmitter eventEmitter;
|
||||||
|
private final ReactExoplayerConfig config;
|
||||||
|
private final DefaultBandwidthMeter bandwidthMeter;
|
||||||
private PlayerControlView playerControlView;
|
private PlayerControlView playerControlView;
|
||||||
private View playPauseControlContainer;
|
private View playPauseControlContainer;
|
||||||
private Player.EventListener eventListener;
|
private Player.EventListener eventListener;
|
||||||
|
|
||||||
private Handler mainHandler;
|
|
||||||
private ExoPlayerView exoPlayerView;
|
private ExoPlayerView exoPlayerView;
|
||||||
|
|
||||||
private DataSource.Factory mediaDataSourceFactory;
|
private DataSource.Factory mediaDataSourceFactory;
|
||||||
@ -116,6 +110,7 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
private boolean isInBackground;
|
private boolean isInBackground;
|
||||||
private boolean isPaused;
|
private boolean isPaused;
|
||||||
private boolean isBuffering;
|
private boolean isBuffering;
|
||||||
|
private boolean muted = false;
|
||||||
private float rate = 1f;
|
private float rate = 1f;
|
||||||
private float audioVolume = 1f;
|
private float audioVolume = 1f;
|
||||||
private int minLoadRetryCount = 3;
|
private int minLoadRetryCount = 3;
|
||||||
@ -135,7 +130,6 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
private Dynamic audioTrackValue;
|
private Dynamic audioTrackValue;
|
||||||
private String videoTrackType;
|
private String videoTrackType;
|
||||||
private Dynamic videoTrackValue;
|
private Dynamic videoTrackValue;
|
||||||
private ReadableArray audioTracks;
|
|
||||||
private String textTrackType;
|
private String textTrackType;
|
||||||
private Dynamic textTrackValue;
|
private Dynamic textTrackValue;
|
||||||
private ReadableArray textTracks;
|
private ReadableArray textTracks;
|
||||||
@ -144,6 +138,7 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
private boolean playInBackground = false;
|
private boolean playInBackground = false;
|
||||||
private Map<String, String> requestHeaders;
|
private Map<String, String> requestHeaders;
|
||||||
private boolean mReportBandwidth = false;
|
private boolean mReportBandwidth = false;
|
||||||
|
private boolean controls;
|
||||||
// \ End props
|
// \ End props
|
||||||
|
|
||||||
// React
|
// React
|
||||||
@ -157,7 +152,7 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
switch (msg.what) {
|
switch (msg.what) {
|
||||||
case SHOW_PROGRESS:
|
case SHOW_PROGRESS:
|
||||||
if (player != null
|
if (player != null
|
||||||
&& player.getPlaybackState() == ExoPlayer.STATE_READY
|
&& player.getPlaybackState() == Player.STATE_READY
|
||||||
&& player.getPlayWhenReady()
|
&& player.getPlayWhenReady()
|
||||||
) {
|
) {
|
||||||
long pos = player.getCurrentPosition();
|
long pos = player.getCurrentPosition();
|
||||||
@ -171,11 +166,12 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public ReactExoplayerView(ThemedReactContext context) {
|
public ReactExoplayerView(ThemedReactContext context, ReactExoplayerConfig config) {
|
||||||
super(context);
|
super(context);
|
||||||
this.themedReactContext = context;
|
this.themedReactContext = context;
|
||||||
|
|
||||||
this.eventEmitter = new VideoEventEmitter(context);
|
this.eventEmitter = new VideoEventEmitter(context);
|
||||||
|
this.config = config;
|
||||||
|
this.bandwidthMeter = config.getBandwidthMeter();
|
||||||
|
|
||||||
createViews();
|
createViews();
|
||||||
|
|
||||||
@ -196,7 +192,6 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
private void createViews() {
|
private void createViews() {
|
||||||
clearResumePosition();
|
clearResumePosition();
|
||||||
mediaDataSourceFactory = buildDataSourceFactory(true);
|
mediaDataSourceFactory = buildDataSourceFactory(true);
|
||||||
mainHandler = new Handler();
|
|
||||||
if (CookieHandler.getDefault() != DEFAULT_COOKIE_MANAGER) {
|
if (CookieHandler.getDefault() != DEFAULT_COOKIE_MANAGER) {
|
||||||
CookieHandler.setDefault(DEFAULT_COOKIE_MANAGER);
|
CookieHandler.setDefault(DEFAULT_COOKIE_MANAGER);
|
||||||
}
|
}
|
||||||
@ -267,6 +262,7 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
* Toggling the visibility of the player control view
|
* Toggling the visibility of the player control view
|
||||||
*/
|
*/
|
||||||
private void togglePlayerControlVisibility() {
|
private void togglePlayerControlVisibility() {
|
||||||
|
if(player == null) return;
|
||||||
reLayout(playerControlView);
|
reLayout(playerControlView);
|
||||||
if (playerControlView.isVisible()) {
|
if (playerControlView.isVisible()) {
|
||||||
playerControlView.hide();
|
playerControlView.hide();
|
||||||
@ -312,10 +308,15 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
* Adding Player control to the frame layout
|
* Adding Player control to the frame layout
|
||||||
*/
|
*/
|
||||||
private void addPlayerControl() {
|
private void addPlayerControl() {
|
||||||
|
if(player == null) return;
|
||||||
LayoutParams layoutParams = new LayoutParams(
|
LayoutParams layoutParams = new LayoutParams(
|
||||||
LayoutParams.MATCH_PARENT,
|
LayoutParams.MATCH_PARENT,
|
||||||
LayoutParams.MATCH_PARENT);
|
LayoutParams.MATCH_PARENT);
|
||||||
playerControlView.setLayoutParams(layoutParams);
|
playerControlView.setLayoutParams(layoutParams);
|
||||||
|
int indexOfPC = indexOfChild(playerControlView);
|
||||||
|
if (indexOfPC != -1) {
|
||||||
|
removeViewAt(indexOfPC);
|
||||||
|
}
|
||||||
addView(playerControlView, 1, layoutParams);
|
addView(playerControlView, 1, layoutParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -333,20 +334,35 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void initializePlayer() {
|
private void initializePlayer() {
|
||||||
|
ReactExoplayerView self = this;
|
||||||
|
// This ensures all props have been settled, to avoid async racing conditions.
|
||||||
|
new Handler().postDelayed(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
if (player == null) {
|
if (player == null) {
|
||||||
TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory(BANDWIDTH_METER);
|
TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory();
|
||||||
trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory);
|
trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory);
|
||||||
trackSelector.setParameters(trackSelector.buildUponParameters()
|
trackSelector.setParameters(trackSelector.buildUponParameters()
|
||||||
.setMaxVideoBitrate(maxBitRate == 0 ? Integer.MAX_VALUE : maxBitRate));
|
.setMaxVideoBitrate(maxBitRate == 0 ? Integer.MAX_VALUE : maxBitRate));
|
||||||
|
|
||||||
DefaultAllocator allocator = new DefaultAllocator(true, C.DEFAULT_BUFFER_SEGMENT_SIZE);
|
DefaultAllocator allocator = new DefaultAllocator(true, C.DEFAULT_BUFFER_SEGMENT_SIZE);
|
||||||
DefaultLoadControl defaultLoadControl = new DefaultLoadControl(allocator, minBufferMs, maxBufferMs, bufferForPlaybackMs, bufferForPlaybackAfterRebufferMs, -1, true);
|
DefaultLoadControl.Builder defaultLoadControlBuilder = new DefaultLoadControl.Builder();
|
||||||
player = ExoPlayerFactory.newSimpleInstance(getContext(), trackSelector, defaultLoadControl);
|
defaultLoadControlBuilder.setAllocator(allocator);
|
||||||
player.addListener(this);
|
defaultLoadControlBuilder.setBufferDurationsMs(minBufferMs, maxBufferMs, bufferForPlaybackMs, bufferForPlaybackAfterRebufferMs);
|
||||||
player.setMetadataOutput(this);
|
defaultLoadControlBuilder.setTargetBufferBytes(-1);
|
||||||
|
defaultLoadControlBuilder.setPrioritizeTimeOverSizeThresholds(true);
|
||||||
|
DefaultLoadControl defaultLoadControl = defaultLoadControlBuilder.createDefaultLoadControl();
|
||||||
|
DefaultRenderersFactory renderersFactory =
|
||||||
|
new DefaultRenderersFactory(getContext())
|
||||||
|
.setExtensionRendererMode(DefaultRenderersFactory.EXTENSION_RENDERER_MODE_OFF);
|
||||||
|
// TODO: Add drmSessionManager to 5th param from: https://github.com/react-native-community/react-native-video/pull/1445
|
||||||
|
player = ExoPlayerFactory.newSimpleInstance(getContext(), renderersFactory,
|
||||||
|
trackSelector, defaultLoadControl, null, bandwidthMeter);
|
||||||
|
player.addListener(self);
|
||||||
|
player.addMetadataOutput(self);
|
||||||
exoPlayerView.setPlayer(player);
|
exoPlayerView.setPlayer(player);
|
||||||
audioBecomingNoisyReceiver.setListener(this);
|
audioBecomingNoisyReceiver.setListener(self);
|
||||||
BANDWIDTH_METER.addEventListener(new Handler(), this);
|
bandwidthMeter.addEventListener(new Handler(), self);
|
||||||
setPlayWhenReady(!isPaused);
|
setPlayWhenReady(!isPaused);
|
||||||
playerNeedsSource = true;
|
playerNeedsSource = true;
|
||||||
|
|
||||||
@ -380,6 +396,10 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
|
|
||||||
// Initializing the playerControlView
|
// Initializing the playerControlView
|
||||||
initializePlayerControl();
|
initializePlayerControl();
|
||||||
|
setControls(controls);
|
||||||
|
applyModifiers();
|
||||||
|
}
|
||||||
|
}, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private MediaSource buildMediaSource(Uri uri, String overrideExtension) {
|
private MediaSource buildMediaSource(Uri uri, String overrideExtension) {
|
||||||
@ -387,21 +407,31 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
: uri.getLastPathSegment());
|
: uri.getLastPathSegment());
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case C.TYPE_SS:
|
case C.TYPE_SS:
|
||||||
return new SsMediaSource(uri, buildDataSourceFactory(false),
|
return new SsMediaSource.Factory(
|
||||||
new DefaultSsChunkSource.Factory(mediaDataSourceFactory),
|
new DefaultSsChunkSource.Factory(mediaDataSourceFactory),
|
||||||
minLoadRetryCount, SsMediaSource.DEFAULT_LIVE_PRESENTATION_DELAY_MS,
|
buildDataSourceFactory(false)
|
||||||
mainHandler, null);
|
).setLoadErrorHandlingPolicy(
|
||||||
|
config.buildLoadErrorHandlingPolicy(minLoadRetryCount)
|
||||||
|
).createMediaSource(uri);
|
||||||
case C.TYPE_DASH:
|
case C.TYPE_DASH:
|
||||||
return new DashMediaSource(uri, buildDataSourceFactory(false),
|
return new DashMediaSource.Factory(
|
||||||
new DefaultDashChunkSource.Factory(mediaDataSourceFactory),
|
new DefaultDashChunkSource.Factory(mediaDataSourceFactory),
|
||||||
minLoadRetryCount, DashMediaSource.DEFAULT_LIVE_PRESENTATION_DELAY_MS,
|
buildDataSourceFactory(false)
|
||||||
mainHandler, null);
|
).setLoadErrorHandlingPolicy(
|
||||||
|
config.buildLoadErrorHandlingPolicy(minLoadRetryCount)
|
||||||
|
).createMediaSource(uri);
|
||||||
case C.TYPE_HLS:
|
case C.TYPE_HLS:
|
||||||
return new HlsMediaSource(uri, mediaDataSourceFactory,
|
return new HlsMediaSource.Factory(
|
||||||
minLoadRetryCount, mainHandler, null);
|
mediaDataSourceFactory
|
||||||
|
).setLoadErrorHandlingPolicy(
|
||||||
|
config.buildLoadErrorHandlingPolicy(minLoadRetryCount)
|
||||||
|
).createMediaSource(uri);
|
||||||
case C.TYPE_OTHER:
|
case C.TYPE_OTHER:
|
||||||
return new ExtractorMediaSource(uri, mediaDataSourceFactory, new DefaultExtractorsFactory(),
|
return new ProgressiveMediaSource.Factory(
|
||||||
mainHandler, null);
|
mediaDataSourceFactory
|
||||||
|
).setLoadErrorHandlingPolicy(
|
||||||
|
config.buildLoadErrorHandlingPolicy(minLoadRetryCount)
|
||||||
|
).createMediaSource(uri);
|
||||||
default: {
|
default: {
|
||||||
throw new IllegalStateException("Unsupported type: " + type);
|
throw new IllegalStateException("Unsupported type: " + type);
|
||||||
}
|
}
|
||||||
@ -431,25 +461,26 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
|
|
||||||
private MediaSource buildTextSource(String title, Uri uri, String mimeType, String language) {
|
private MediaSource buildTextSource(String title, Uri uri, String mimeType, String language) {
|
||||||
Format textFormat = Format.createTextSampleFormat(title, mimeType, Format.NO_VALUE, language);
|
Format textFormat = Format.createTextSampleFormat(title, mimeType, Format.NO_VALUE, language);
|
||||||
return new SingleSampleMediaSource(uri, mediaDataSourceFactory, textFormat, C.TIME_UNSET);
|
return new SingleSampleMediaSource.Factory(mediaDataSourceFactory)
|
||||||
|
.createMediaSource(uri, textFormat, C.TIME_UNSET);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void releasePlayer() {
|
private void releasePlayer() {
|
||||||
if (player != null) {
|
if (player != null) {
|
||||||
updateResumePosition();
|
updateResumePosition();
|
||||||
player.release();
|
player.release();
|
||||||
player.setMetadataOutput(null);
|
player.removeMetadataOutput(this);
|
||||||
player = null;
|
|
||||||
trackSelector = null;
|
trackSelector = null;
|
||||||
|
player = null;
|
||||||
}
|
}
|
||||||
progressHandler.removeMessages(SHOW_PROGRESS);
|
progressHandler.removeMessages(SHOW_PROGRESS);
|
||||||
themedReactContext.removeLifecycleEventListener(this);
|
themedReactContext.removeLifecycleEventListener(this);
|
||||||
audioBecomingNoisyReceiver.removeListener();
|
audioBecomingNoisyReceiver.removeListener();
|
||||||
BANDWIDTH_METER.removeEventListener(this);
|
bandwidthMeter.removeEventListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean requestAudioFocus() {
|
private boolean requestAudioFocus() {
|
||||||
if (disableFocus) {
|
if (disableFocus || srcUri == null) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
int result = audioManager.requestAudioFocus(this,
|
int result = audioManager.requestAudioFocus(this,
|
||||||
@ -476,12 +507,12 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
private void startPlayback() {
|
private void startPlayback() {
|
||||||
if (player != null) {
|
if (player != null) {
|
||||||
switch (player.getPlaybackState()) {
|
switch (player.getPlaybackState()) {
|
||||||
case ExoPlayer.STATE_IDLE:
|
case Player.STATE_IDLE:
|
||||||
case ExoPlayer.STATE_ENDED:
|
case Player.STATE_ENDED:
|
||||||
initializePlayer();
|
initializePlayer();
|
||||||
break;
|
break;
|
||||||
case ExoPlayer.STATE_BUFFERING:
|
case Player.STATE_BUFFERING:
|
||||||
case ExoPlayer.STATE_READY:
|
case Player.STATE_READY:
|
||||||
if (!player.getPlayWhenReady()) {
|
if (!player.getPlayWhenReady()) {
|
||||||
setPlayWhenReady(true);
|
setPlayWhenReady(true);
|
||||||
}
|
}
|
||||||
@ -534,12 +565,13 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
/**
|
/**
|
||||||
* Returns a new DataSource factory.
|
* Returns a new DataSource factory.
|
||||||
*
|
*
|
||||||
* @param useBandwidthMeter Whether to set {@link #BANDWIDTH_METER} as a listener to the new
|
* @param useBandwidthMeter Whether to set {@link #bandwidthMeter} as a listener to the new
|
||||||
* DataSource factory.
|
* DataSource factory.
|
||||||
* @return A new DataSource factory.
|
* @return A new DataSource factory.
|
||||||
*/
|
*/
|
||||||
private DataSource.Factory buildDataSourceFactory(boolean useBandwidthMeter) {
|
private DataSource.Factory buildDataSourceFactory(boolean useBandwidthMeter) {
|
||||||
return DataSourceUtil.getDefaultDataSourceFactory(this.themedReactContext, useBandwidthMeter ? BANDWIDTH_METER : null, requestHeaders);
|
return DataSourceUtil.getDefaultDataSourceFactory(this.themedReactContext,
|
||||||
|
useBandwidthMeter ? bandwidthMeter : null, requestHeaders);
|
||||||
}
|
}
|
||||||
|
|
||||||
// AudioManager.OnAudioFocusChangeListener implementation
|
// AudioManager.OnAudioFocusChangeListener implementation
|
||||||
@ -560,13 +592,17 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
if (player != null) {
|
if (player != null) {
|
||||||
if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {
|
if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {
|
||||||
// Lower the volume
|
// Lower the volume
|
||||||
|
if (!muted) {
|
||||||
player.setVolume(audioVolume * 0.8f);
|
player.setVolume(audioVolume * 0.8f);
|
||||||
|
}
|
||||||
} else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
|
} else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
|
||||||
// Raise it back to normal
|
// Raise it back to normal
|
||||||
|
if (!muted) {
|
||||||
player.setVolume(audioVolume * 1);
|
player.setVolume(audioVolume * 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// AudioBecomingNoisyListener implementation
|
// AudioBecomingNoisyListener implementation
|
||||||
|
|
||||||
@ -575,7 +611,7 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
eventEmitter.audioBecomingNoisy();
|
eventEmitter.audioBecomingNoisy();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExoPlayer.EventListener implementation
|
// Player.EventListener implementation
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onLoadingChanged(boolean isLoading) {
|
public void onLoadingChanged(boolean isLoading) {
|
||||||
@ -586,26 +622,26 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
|
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
|
||||||
String text = "onStateChanged: playWhenReady=" + playWhenReady + ", playbackState=";
|
String text = "onStateChanged: playWhenReady=" + playWhenReady + ", playbackState=";
|
||||||
switch (playbackState) {
|
switch (playbackState) {
|
||||||
case ExoPlayer.STATE_IDLE:
|
case Player.STATE_IDLE:
|
||||||
text += "idle";
|
text += "idle";
|
||||||
eventEmitter.idle();
|
eventEmitter.idle();
|
||||||
break;
|
break;
|
||||||
case ExoPlayer.STATE_BUFFERING:
|
case Player.STATE_BUFFERING:
|
||||||
text += "buffering";
|
text += "buffering";
|
||||||
onBuffering(true);
|
onBuffering(true);
|
||||||
break;
|
break;
|
||||||
case ExoPlayer.STATE_READY:
|
case Player.STATE_READY:
|
||||||
text += "ready";
|
text += "ready";
|
||||||
eventEmitter.ready();
|
eventEmitter.ready();
|
||||||
onBuffering(false);
|
onBuffering(false);
|
||||||
startProgressHandler();
|
startProgressHandler();
|
||||||
videoLoaded();
|
videoLoaded();
|
||||||
//Setting the visibility for the playerControlView
|
//Setting the visibility for the playerControlView
|
||||||
if(playerControlView != null) {
|
if (playerControlView != null) {
|
||||||
playerControlView.show();
|
playerControlView.show();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ExoPlayer.STATE_ENDED:
|
case Player.STATE_ENDED:
|
||||||
text += "ended";
|
text += "ended";
|
||||||
eventEmitter.end();
|
eventEmitter.end();
|
||||||
onStopPlayback();
|
onStopPlayback();
|
||||||
@ -731,7 +767,7 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
}
|
}
|
||||||
// When repeat is turned on, reaching the end of the video will not cause a state change
|
// When repeat is turned on, reaching the end of the video will not cause a state change
|
||||||
// so we need to explicitly detect it.
|
// so we need to explicitly detect it.
|
||||||
if (reason == ExoPlayer.DISCONTINUITY_REASON_PERIOD_TRANSITION
|
if (reason == Player.DISCONTINUITY_REASON_PERIOD_TRANSITION
|
||||||
&& player.getRepeatMode() == Player.REPEAT_MODE_ONE) {
|
&& player.getRepeatMode() == Player.REPEAT_MODE_ONE) {
|
||||||
eventEmitter.end();
|
eventEmitter.end();
|
||||||
}
|
}
|
||||||
@ -849,7 +885,9 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
this.srcUri = uri;
|
this.srcUri = uri;
|
||||||
this.extension = extension;
|
this.extension = extension;
|
||||||
this.requestHeaders = headers;
|
this.requestHeaders = headers;
|
||||||
this.mediaDataSourceFactory = DataSourceUtil.getDefaultDataSourceFactory(this.themedReactContext, BANDWIDTH_METER, this.requestHeaders);
|
this.mediaDataSourceFactory =
|
||||||
|
DataSourceUtil.getDefaultDataSourceFactory(this.themedReactContext, bandwidthMeter,
|
||||||
|
this.requestHeaders);
|
||||||
|
|
||||||
if (!isOriginalSourceNull && !isSourceEqual) {
|
if (!isOriginalSourceNull && !isSourceEqual) {
|
||||||
reloadSource();
|
reloadSource();
|
||||||
@ -894,6 +932,11 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
exoPlayerView.setResizeMode(resizeMode);
|
exoPlayerView.setResizeMode(resizeMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void applyModifiers() {
|
||||||
|
setRepeatModifier(repeat);
|
||||||
|
setMutedModifier(muted);
|
||||||
|
}
|
||||||
|
|
||||||
public void setRepeatModifier(boolean repeat) {
|
public void setRepeatModifier(boolean repeat) {
|
||||||
if (player != null) {
|
if (player != null) {
|
||||||
if (repeat) {
|
if (repeat) {
|
||||||
@ -906,6 +949,7 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void setSelectedTrack(int trackType, String type, Dynamic value) {
|
public void setSelectedTrack(int trackType, String type, Dynamic value) {
|
||||||
|
if (player == null) return;
|
||||||
int rendererIndex = getTrackRendererIndex(trackType);
|
int rendererIndex = getTrackRendererIndex(trackType);
|
||||||
if (rendererIndex == C.INDEX_UNSET) {
|
if (rendererIndex == C.INDEX_UNSET) {
|
||||||
return;
|
return;
|
||||||
@ -1048,6 +1092,7 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void setMutedModifier(boolean muted) {
|
public void setMutedModifier(boolean muted) {
|
||||||
|
this.muted = muted;
|
||||||
audioVolume = muted ? 0.f : 1.f;
|
audioVolume = muted ? 0.f : 1.f;
|
||||||
if (player != null) {
|
if (player != null) {
|
||||||
player.setVolume(audioVolume);
|
player.setVolume(audioVolume);
|
||||||
@ -1156,10 +1201,15 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
* @param controls Controls prop, if true enable controls, if false disable them
|
* @param controls Controls prop, if true enable controls, if false disable them
|
||||||
*/
|
*/
|
||||||
public void setControls(boolean controls) {
|
public void setControls(boolean controls) {
|
||||||
if (controls && exoPlayerView != null) {
|
this.controls = controls;
|
||||||
|
if (player == null || exoPlayerView == null) return;
|
||||||
|
if (controls) {
|
||||||
addPlayerControl();
|
addPlayerControl();
|
||||||
} else if (getChildAt(1) instanceof PlayerControlView && exoPlayerView != null) {
|
} else {
|
||||||
removeViewAt(1);
|
int indexOfPC = indexOfChild(playerControlView);
|
||||||
|
if (indexOfPC != -1) {
|
||||||
|
removeViewAt(indexOfPC);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -60,6 +60,12 @@ 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 ReactExoplayerConfig config;
|
||||||
|
|
||||||
|
public ReactExoplayerViewManager(ReactExoplayerConfig config) {
|
||||||
|
this.config = config;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return REACT_CLASS;
|
return REACT_CLASS;
|
||||||
@ -67,7 +73,7 @@ public class ReactExoplayerViewManager extends ViewGroupManager<ReactExoplayerVi
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected ReactExoplayerView createViewInstance(ThemedReactContext themedReactContext) {
|
protected ReactExoplayerView createViewInstance(ThemedReactContext themedReactContext) {
|
||||||
return new ReactExoplayerView(themedReactContext);
|
return new ReactExoplayerView(themedReactContext, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package com.brentvatne.exoplayer;
|
package com.brentvatne.exoplayer;
|
||||||
|
|
||||||
import android.support.annotation.IntDef;
|
import androidx.annotation.IntDef;
|
||||||
|
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package com.brentvatne.exoplayer;
|
package com.brentvatne.exoplayer;
|
||||||
|
|
||||||
import android.support.annotation.StringDef;
|
import androidx.annotation.StringDef;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
import com.facebook.react.bridge.Arguments;
|
import com.facebook.react.bridge.Arguments;
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package com.brentvatne.react;
|
package com.brentvatne.react;
|
||||||
|
|
||||||
|
import com.brentvatne.exoplayer.DefaultReactExoplayerConfig;
|
||||||
|
import com.brentvatne.exoplayer.ReactExoplayerConfig;
|
||||||
import com.brentvatne.exoplayer.ReactExoplayerViewManager;
|
import com.brentvatne.exoplayer.ReactExoplayerViewManager;
|
||||||
import com.facebook.react.ReactPackage;
|
import com.facebook.react.ReactPackage;
|
||||||
import com.facebook.react.bridge.JavaScriptModule;
|
import com.facebook.react.bridge.JavaScriptModule;
|
||||||
@ -12,6 +14,15 @@ import java.util.List;
|
|||||||
|
|
||||||
public class ReactVideoPackage implements ReactPackage {
|
public class ReactVideoPackage implements ReactPackage {
|
||||||
|
|
||||||
|
private ReactExoplayerConfig config;
|
||||||
|
|
||||||
|
public ReactVideoPackage() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReactVideoPackage(ReactExoplayerConfig config) {
|
||||||
|
this.config = config;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
|
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
@ -25,6 +36,9 @@ public class ReactVideoPackage implements ReactPackage {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
|
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
|
||||||
return Collections.<ViewManager>singletonList(new ReactExoplayerViewManager());
|
if (config == null) {
|
||||||
|
config = new DefaultReactExoplayerConfig(reactContext);
|
||||||
|
}
|
||||||
|
return Collections.singletonList(new ReactExoplayerViewManager(config));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,12 +5,12 @@ def safeExtGet(prop, fallback) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion safeExtGet('compileSdkVersion', 27)
|
compileSdkVersion safeExtGet('compileSdkVersion', 28)
|
||||||
buildToolsVersion safeExtGet('buildToolsVersion', '27.0.3')
|
buildToolsVersion safeExtGet('buildToolsVersion', '28.0.3')
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion safeExtGet('minSdkVersion', 16)
|
minSdkVersion safeExtGet('minSdkVersion', 16)
|
||||||
targetSdkVersion safeExtGet('targetSdkVersion', 27)
|
targetSdkVersion safeExtGet('targetSdkVersion', 28)
|
||||||
versionCode 1
|
versionCode 1
|
||||||
versionName "1.0"
|
versionName "1.0"
|
||||||
ndk {
|
ndk {
|
||||||
@ -21,6 +21,6 @@ android {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
//noinspection GradleDynamicVersion
|
//noinspection GradleDynamicVersion
|
||||||
compileOnly "com.facebook.react:react-native:${safeExtGet('reactNativeVersion', '+')}"
|
implementation "com.facebook.react:react-native:${safeExtGet('reactNativeVersion', '+')}"
|
||||||
implementation 'com.yqritc:android-scalablevideoview:1.0.4'
|
implementation 'com.yqritc:android-scalablevideoview:1.0.4'
|
||||||
}
|
}
|
||||||
|
@ -37,6 +37,7 @@ class RCTVideo extends RCTView {
|
|||||||
this.videoElement = this.initializeVideoElement();
|
this.videoElement = this.initializeVideoElement();
|
||||||
this.videoElement.addEventListener("ended", this.onEnd);
|
this.videoElement.addEventListener("ended", this.onEnd);
|
||||||
this.videoElement.addEventListener("loadeddata", this.onLoad);
|
this.videoElement.addEventListener("loadeddata", this.onLoad);
|
||||||
|
this.videoElement.addEventListener("canplay", this.onReadyForDisplay);
|
||||||
this.videoElement.addEventListener("loadstart", this.onLoadStart);
|
this.videoElement.addEventListener("loadstart", this.onLoadStart);
|
||||||
this.videoElement.addEventListener("pause", this.onPause);
|
this.videoElement.addEventListener("pause", this.onPause);
|
||||||
this.videoElement.addEventListener("play", this.onPlay);
|
this.videoElement.addEventListener("play", this.onPlay);
|
||||||
@ -51,6 +52,7 @@ class RCTVideo extends RCTView {
|
|||||||
detachFromView(view: UIView) {
|
detachFromView(view: UIView) {
|
||||||
this.videoElement.removeEventListener("ended", this.onEnd);
|
this.videoElement.removeEventListener("ended", this.onEnd);
|
||||||
this.videoElement.removeEventListener("loadeddata", this.onLoad);
|
this.videoElement.removeEventListener("loadeddata", this.onLoad);
|
||||||
|
this.videoElement.removeEventListener("canplay", this.onReadyForDisplay);
|
||||||
this.videoElement.removeEventListener("loadstart", this.onLoadStart);
|
this.videoElement.removeEventListener("loadstart", this.onLoadStart);
|
||||||
this.videoElement.removeEventListener("pause", this.onPause);
|
this.videoElement.removeEventListener("pause", this.onPause);
|
||||||
this.videoElement.removeEventListener("play", this.onPlay);
|
this.videoElement.removeEventListener("play", this.onPlay);
|
||||||
@ -203,6 +205,10 @@ class RCTVideo extends RCTView {
|
|||||||
this.sendEvent("topVideoLoad", payload);
|
this.sendEvent("topVideoLoad", payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onReadyForDisplay = () => {
|
||||||
|
this.sendEvent("onReadyForDisplay");
|
||||||
|
}
|
||||||
|
|
||||||
onLoadStart = () => {
|
onLoadStart = () => {
|
||||||
const src = this.videoElement.currentSrc;
|
const src = this.videoElement.currentSrc;
|
||||||
const payload = {
|
const payload = {
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
"presets": ["react-native"]
|
|
||||||
}
|
|
@ -65,6 +65,10 @@ import com.android.build.OutputFile
|
|||||||
* ]
|
* ]
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
project.ext.react = [
|
||||||
|
entryFile: "index.android.js",
|
||||||
|
enableHermes: false,
|
||||||
|
]
|
||||||
apply from: "../../node_modules/react-native/react.gradle"
|
apply from: "../../node_modules/react-native/react.gradle"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -83,13 +87,16 @@ def enableSeparateBuildPerCPUArchitecture = false
|
|||||||
def enableProguardInReleaseBuilds = false
|
def enableProguardInReleaseBuilds = false
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 25
|
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||||
buildToolsVersion '25.0.2'
|
buildToolsVersion rootProject.ext.buildToolsVersion
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "com.videoplayer"
|
applicationId "com.videoplayer"
|
||||||
minSdkVersion 16
|
minSdkVersion rootProject.ext.minSdkVersion
|
||||||
targetSdkVersion 25
|
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||||
versionCode 1
|
versionCode 1
|
||||||
versionName "1.0"
|
versionName "1.0"
|
||||||
ndk {
|
ndk {
|
||||||
@ -108,6 +115,7 @@ android {
|
|||||||
release {
|
release {
|
||||||
signingConfig signingConfigs.debug
|
signingConfig signingConfigs.debug
|
||||||
minifyEnabled enableProguardInReleaseBuilds
|
minifyEnabled enableProguardInReleaseBuilds
|
||||||
|
matchingFallbacks = ['release', 'debug']
|
||||||
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
|
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -127,10 +135,9 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile "com.android.support:appcompat-v7:25.2.0"
|
implementation project(':react-native-video')
|
||||||
compile "com.facebook.react:react-native:+" // From node_modules
|
implementation "com.facebook.react:react-native:+" // From node_modules
|
||||||
compile project(':react-native-video')
|
implementation 'org.webkit:android-jsc:+'
|
||||||
// compile project(':react-native-video-exoplayer') // uncomment to use exoplayer
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run this once to be able to run the application with BUCK
|
// Run this once to be able to run the application with BUCK
|
||||||
|
@ -6,10 +6,6 @@
|
|||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
|
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
|
||||||
|
|
||||||
<uses-sdk
|
|
||||||
android:minSdkVersion="16"
|
|
||||||
android:targetSdkVersion="22" />
|
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".MainApplication"
|
android:name=".MainApplication"
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
|
@ -2,8 +2,8 @@ package com.videoplayer;
|
|||||||
|
|
||||||
import android.app.Application;
|
import android.app.Application;
|
||||||
|
|
||||||
import com.brentvatne.react.ReactVideoPackage;
|
|
||||||
import com.facebook.react.ReactApplication;
|
import com.facebook.react.ReactApplication;
|
||||||
|
import com.brentvatne.react.ReactVideoPackage;
|
||||||
import com.facebook.react.ReactNativeHost;
|
import com.facebook.react.ReactNativeHost;
|
||||||
import com.facebook.react.ReactPackage;
|
import com.facebook.react.ReactPackage;
|
||||||
import com.facebook.react.shell.MainReactPackage;
|
import com.facebook.react.shell.MainReactPackage;
|
||||||
|
@ -1,11 +1,20 @@
|
|||||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||||
|
|
||||||
buildscript {
|
buildscript {
|
||||||
|
ext {
|
||||||
|
buildToolsVersion = "28.0.3"
|
||||||
|
minSdkVersion = 16
|
||||||
|
compileSdkVersion = 28
|
||||||
|
targetSdkVersion = 28
|
||||||
|
supportLibVersion = "28.0.0"
|
||||||
|
|
||||||
|
}
|
||||||
repositories {
|
repositories {
|
||||||
jcenter()
|
jcenter()
|
||||||
|
google()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:2.3.0'
|
classpath 'com.android.tools.build:gradle:3.4.1'
|
||||||
|
|
||||||
// NOTE: Do not place your application dependencies here; they belong
|
// NOTE: Do not place your application dependencies here; they belong
|
||||||
// in the individual module build.gradle files
|
// in the individual module build.gradle files
|
||||||
@ -15,10 +24,18 @@ buildscript {
|
|||||||
allprojects {
|
allprojects {
|
||||||
repositories {
|
repositories {
|
||||||
mavenLocal()
|
mavenLocal()
|
||||||
|
google()
|
||||||
|
maven {
|
||||||
|
url "https://jitpack.io"
|
||||||
|
}
|
||||||
jcenter()
|
jcenter()
|
||||||
maven {
|
maven {
|
||||||
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
|
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
|
||||||
url "$rootDir/../node_modules/react-native/android"
|
url "$rootDir/../node_modules/react-native/android"
|
||||||
}
|
}
|
||||||
|
maven {
|
||||||
|
// Android JSC is installed from npm
|
||||||
|
url("$rootDir/../node_modules/jsc-android/dist")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,4 +17,5 @@
|
|||||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||||
# org.gradle.parallel=true
|
# org.gradle.parallel=true
|
||||||
|
|
||||||
android.useDeprecatedNdk=true
|
android.useAndroidX=true
|
||||||
|
android.enableJetifier=true
|
@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
|
|||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
|
||||||
|
@ -1,12 +1,6 @@
|
|||||||
rootProject.name = 'VideoPlayer'
|
rootProject.name = 'VideoPlayer'
|
||||||
|
|
||||||
include ':app',
|
include ':react-native-video'
|
||||||
':react-native-video',
|
project(':react-native-video').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-video/android-exoplayer')
|
||||||
':react-native-video-exoplayer'
|
|
||||||
|
|
||||||
// Quick Local Development
|
include ':app'
|
||||||
//project(':react-native-video').projectDir = new File(rootProject.projectDir, '../../android')
|
|
||||||
//project(':react-native-video-exoplayer').projectDir = new File(rootProject.projectDir, '../../android-exoplayer')
|
|
||||||
|
|
||||||
project(':react-native-video').projectDir = new File(rootProject.projectDir, '../../android')
|
|
||||||
project(':react-native-video-exoplayer').projectDir = new File(rootProject.projectDir, '../../android-exoplayer')
|
|
||||||
|
3
examples/basic/babel.config.js
Normal file
3
examples/basic/babel.config.js
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
module.exports = {
|
||||||
|
presets: ['module:metro-react-native-babel-preset'],
|
||||||
|
};
|
10
examples/basic/metro.config.js
Normal file
10
examples/basic/metro.config.js
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
module.exports = {
|
||||||
|
transformer: {
|
||||||
|
getTransformOptions: async () => ({
|
||||||
|
transform: {
|
||||||
|
experimentalImportSupport: false,
|
||||||
|
inlineRequires: false,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
};
|
@ -4,18 +4,23 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node node_modules/react-native/local-cli/cli.js start",
|
"start": "node node_modules/react-native/local-cli/cli.js start",
|
||||||
"test": "jest"
|
"postinstall": "rm -rf node_modules/react-native-video/{examples,node_modules}",
|
||||||
|
"test": "jest",
|
||||||
|
"lint": "eslint ."
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"react": "16.4.1",
|
"react": "16.9.0",
|
||||||
"react-native": "0.56.0",
|
"react-native": "0.60.5",
|
||||||
"react-native-video": "file:../.."
|
"react-native-video": "file:../.."
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"babel-jest": "22.4.1",
|
"@babel/core": "^7.6.0",
|
||||||
"babel-preset-react-native": "5.0.2",
|
"@babel/runtime": "^7.6.0",
|
||||||
"express": "^4.16.2",
|
"@react-native-community/eslint-config": "^0.0.5",
|
||||||
"jest": "22.4.2",
|
"babel-jest": "^24.9.0",
|
||||||
"react-test-renderer": "16.2.0"
|
"eslint": "^6.4.0",
|
||||||
|
"jest": "^24.9.0",
|
||||||
|
"metro-react-native-babel-preset": "^0.56.0",
|
||||||
|
"react-test-renderer": "16.8.6"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +0,0 @@
|
|||||||
const blacklist = require('metro').createBlacklist;
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
getBlacklistRE: function() {
|
|
||||||
return blacklist([/node_modules\/react-native-video\/examples\/.*/]);
|
|
||||||
}
|
|
||||||
};
|
|
File diff suppressed because it is too large
Load Diff
@ -21,27 +21,27 @@
|
|||||||
@interface RCTVideo : UIView <RCTVideoPlayerViewControllerDelegate, AVPictureInPictureControllerDelegate>
|
@interface RCTVideo : UIView <RCTVideoPlayerViewControllerDelegate, AVPictureInPictureControllerDelegate>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@property (nonatomic, copy) RCTBubblingEventBlock onVideoLoadStart;
|
@property (nonatomic, copy) RCTDirectEventBlock onVideoLoadStart;
|
||||||
@property (nonatomic, copy) RCTBubblingEventBlock onVideoLoad;
|
@property (nonatomic, copy) RCTDirectEventBlock onVideoLoad;
|
||||||
@property (nonatomic, copy) RCTBubblingEventBlock onVideoBuffer;
|
@property (nonatomic, copy) RCTDirectEventBlock onVideoBuffer;
|
||||||
@property (nonatomic, copy) RCTBubblingEventBlock onVideoError;
|
@property (nonatomic, copy) RCTDirectEventBlock onVideoError;
|
||||||
@property (nonatomic, copy) RCTBubblingEventBlock onVideoProgress;
|
@property (nonatomic, copy) RCTDirectEventBlock onVideoProgress;
|
||||||
@property (nonatomic, copy) RCTBubblingEventBlock onBandwidthUpdate;
|
@property (nonatomic, copy) RCTDirectEventBlock onBandwidthUpdate;
|
||||||
@property (nonatomic, copy) RCTBubblingEventBlock onVideoSeek;
|
@property (nonatomic, copy) RCTDirectEventBlock onVideoSeek;
|
||||||
@property (nonatomic, copy) RCTBubblingEventBlock onVideoEnd;
|
@property (nonatomic, copy) RCTDirectEventBlock onVideoEnd;
|
||||||
@property (nonatomic, copy) RCTBubblingEventBlock onTimedMetadata;
|
@property (nonatomic, copy) RCTDirectEventBlock onTimedMetadata;
|
||||||
@property (nonatomic, copy) RCTBubblingEventBlock onVideoAudioBecomingNoisy;
|
@property (nonatomic, copy) RCTDirectEventBlock onVideoAudioBecomingNoisy;
|
||||||
@property (nonatomic, copy) RCTBubblingEventBlock onVideoFullscreenPlayerWillPresent;
|
@property (nonatomic, copy) RCTDirectEventBlock onVideoFullscreenPlayerWillPresent;
|
||||||
@property (nonatomic, copy) RCTBubblingEventBlock onVideoFullscreenPlayerDidPresent;
|
@property (nonatomic, copy) RCTDirectEventBlock onVideoFullscreenPlayerDidPresent;
|
||||||
@property (nonatomic, copy) RCTBubblingEventBlock onVideoFullscreenPlayerWillDismiss;
|
@property (nonatomic, copy) RCTDirectEventBlock onVideoFullscreenPlayerWillDismiss;
|
||||||
@property (nonatomic, copy) RCTBubblingEventBlock onVideoFullscreenPlayerDidDismiss;
|
@property (nonatomic, copy) RCTDirectEventBlock onVideoFullscreenPlayerDidDismiss;
|
||||||
@property (nonatomic, copy) RCTBubblingEventBlock onReadyForDisplay;
|
@property (nonatomic, copy) RCTDirectEventBlock onReadyForDisplay;
|
||||||
@property (nonatomic, copy) RCTBubblingEventBlock onPlaybackStalled;
|
@property (nonatomic, copy) RCTDirectEventBlock onPlaybackStalled;
|
||||||
@property (nonatomic, copy) RCTBubblingEventBlock onPlaybackResume;
|
@property (nonatomic, copy) RCTDirectEventBlock onPlaybackResume;
|
||||||
@property (nonatomic, copy) RCTBubblingEventBlock onPlaybackRateChange;
|
@property (nonatomic, copy) RCTDirectEventBlock onPlaybackRateChange;
|
||||||
@property (nonatomic, copy) RCTBubblingEventBlock onVideoExternalPlaybackChange;
|
@property (nonatomic, copy) RCTDirectEventBlock onVideoExternalPlaybackChange;
|
||||||
@property (nonatomic, copy) RCTBubblingEventBlock onPictureInPictureStatusChanged;
|
@property (nonatomic, copy) RCTDirectEventBlock onPictureInPictureStatusChanged;
|
||||||
@property (nonatomic, copy) RCTBubblingEventBlock onRestoreUserInterfaceForPictureInPictureStop;
|
@property (nonatomic, copy) RCTDirectEventBlock onRestoreUserInterfaceForPictureInPictureStop;
|
||||||
|
|
||||||
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER;
|
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER;
|
||||||
|
|
||||||
|
@ -54,6 +54,7 @@ static int const RCTVideoUnset = -1;
|
|||||||
float _rate;
|
float _rate;
|
||||||
float _maxBitRate;
|
float _maxBitRate;
|
||||||
|
|
||||||
|
BOOL _automaticallyWaitsToMinimizeStalling;
|
||||||
BOOL _muted;
|
BOOL _muted;
|
||||||
BOOL _paused;
|
BOOL _paused;
|
||||||
BOOL _repeat;
|
BOOL _repeat;
|
||||||
@ -87,7 +88,7 @@ static int const RCTVideoUnset = -1;
|
|||||||
{
|
{
|
||||||
if ((self = [super init])) {
|
if ((self = [super init])) {
|
||||||
_eventDispatcher = eventDispatcher;
|
_eventDispatcher = eventDispatcher;
|
||||||
|
_automaticallyWaitsToMinimizeStalling = YES;
|
||||||
_playbackRateObserverRegistered = NO;
|
_playbackRateObserverRegistered = NO;
|
||||||
_isExternalPlaybackActiveObserverRegistered = NO;
|
_isExternalPlaybackActiveObserverRegistered = NO;
|
||||||
_playbackStalled = NO;
|
_playbackStalled = NO;
|
||||||
@ -223,6 +224,7 @@ static int const RCTVideoUnset = -1;
|
|||||||
if (_playInBackground) {
|
if (_playInBackground) {
|
||||||
// Needed to play sound in background. See https://developer.apple.com/library/ios/qa/qa1668/_index.html
|
// Needed to play sound in background. See https://developer.apple.com/library/ios/qa/qa1668/_index.html
|
||||||
[_playerLayer setPlayer:nil];
|
[_playerLayer setPlayer:nil];
|
||||||
|
[_playerViewController setPlayer:nil];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -231,6 +233,7 @@ static int const RCTVideoUnset = -1;
|
|||||||
[self applyModifiers];
|
[self applyModifiers];
|
||||||
if (_playInBackground) {
|
if (_playInBackground) {
|
||||||
[_playerLayer setPlayer:_player];
|
[_playerLayer setPlayer:_player];
|
||||||
|
[_playerViewController setPlayer:_player];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -354,8 +357,6 @@ static int const RCTVideoUnset = -1;
|
|||||||
[self setMaxBitRate:_maxBitRate];
|
[self setMaxBitRate:_maxBitRate];
|
||||||
|
|
||||||
[_player pause];
|
[_player pause];
|
||||||
[_playerViewController.view removeFromSuperview];
|
|
||||||
_playerViewController = nil;
|
|
||||||
|
|
||||||
if (_playbackRateObserverRegistered) {
|
if (_playbackRateObserverRegistered) {
|
||||||
[_player removeObserver:self forKeyPath:playbackRate context:nil];
|
[_player removeObserver:self forKeyPath:playbackRate context:nil];
|
||||||
@ -376,6 +377,9 @@ static int const RCTVideoUnset = -1;
|
|||||||
_isExternalPlaybackActiveObserverRegistered = YES;
|
_isExternalPlaybackActiveObserverRegistered = YES;
|
||||||
|
|
||||||
[self addPlayerTimeObserver];
|
[self addPlayerTimeObserver];
|
||||||
|
if (@available(iOS 10.0, *)) {
|
||||||
|
[self setAutomaticallyWaitsToMinimizeStalling:_automaticallyWaitsToMinimizeStalling];
|
||||||
|
}
|
||||||
|
|
||||||
//Perform on next run loop, otherwise onVideoLoadStart is nil
|
//Perform on next run loop, otherwise onVideoLoadStart is nil
|
||||||
if (self.onVideoLoadStart) {
|
if (self.onVideoLoadStart) {
|
||||||
@ -578,27 +582,11 @@ static int const RCTVideoUnset = -1;
|
|||||||
|
|
||||||
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
|
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
|
||||||
{
|
{
|
||||||
// when controls==true, this is a hack to reset the rootview when rotation happens in fullscreen
|
|
||||||
if (object == _playerViewController.contentOverlayView) {
|
|
||||||
if ([keyPath isEqualToString:@"frame"]) {
|
|
||||||
|
|
||||||
CGRect oldRect = [change[NSKeyValueChangeOldKey] CGRectValue];
|
|
||||||
CGRect newRect = [change[NSKeyValueChangeNewKey] CGRectValue];
|
|
||||||
|
|
||||||
if (!CGRectEqualToRect(oldRect, newRect)) {
|
|
||||||
if (CGRectEqualToRect(newRect, [UIScreen mainScreen].bounds)) {
|
|
||||||
NSLog(@"in fullscreen");
|
|
||||||
} else NSLog(@"not fullscreen");
|
|
||||||
|
|
||||||
[self.reactViewController.view setFrame:[UIScreen mainScreen].bounds];
|
|
||||||
[self.reactViewController.view setNeedsLayout];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if([keyPath isEqualToString:readyForDisplayKeyPath] && [change objectForKey:NSKeyValueChangeNewKey] && self.onReadyForDisplay) {
|
||||||
|
self.onReadyForDisplay(@{@"target": self.reactTag});
|
||||||
return;
|
return;
|
||||||
} else
|
|
||||||
return [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (object == _playerItem) {
|
if (object == _playerItem) {
|
||||||
// When timeMetadata is read the event onTimedMetadata is triggered
|
// When timeMetadata is read the event onTimedMetadata is triggered
|
||||||
if ([keyPath isEqualToString:timedMetadata]) {
|
if ([keyPath isEqualToString:timedMetadata]) {
|
||||||
@ -690,12 +678,6 @@ static int const RCTVideoUnset = -1;
|
|||||||
_playerBufferEmpty = NO;
|
_playerBufferEmpty = NO;
|
||||||
self.onVideoBuffer(@{@"isBuffering": @(NO), @"target": self.reactTag});
|
self.onVideoBuffer(@{@"isBuffering": @(NO), @"target": self.reactTag});
|
||||||
}
|
}
|
||||||
} else if (object == _playerLayer) {
|
|
||||||
if([keyPath isEqualToString:readyForDisplayKeyPath] && [change objectForKey:NSKeyValueChangeNewKey]) {
|
|
||||||
if([change objectForKey:NSKeyValueChangeNewKey] && self.onReadyForDisplay) {
|
|
||||||
self.onReadyForDisplay(@{@"target": self.reactTag});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (object == _player) {
|
} else if (object == _player) {
|
||||||
if([keyPath isEqualToString:playbackRate]) {
|
if([keyPath isEqualToString:playbackRate]) {
|
||||||
if(self.onPlaybackRateChange) {
|
if(self.onPlaybackRateChange) {
|
||||||
@ -716,8 +698,24 @@ static int const RCTVideoUnset = -1;
|
|||||||
@"target": self.reactTag});
|
@"target": self.reactTag});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else if (object == _playerViewController.contentOverlayView) {
|
||||||
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
|
// when controls==true, this is a hack to reset the rootview when rotation happens in fullscreen
|
||||||
|
if ([keyPath isEqualToString:@"frame"]) {
|
||||||
|
|
||||||
|
CGRect oldRect = [change[NSKeyValueChangeOldKey] CGRectValue];
|
||||||
|
CGRect newRect = [change[NSKeyValueChangeNewKey] CGRectValue];
|
||||||
|
|
||||||
|
if (!CGRectEqualToRect(oldRect, newRect)) {
|
||||||
|
if (CGRectEqualToRect(newRect, [UIScreen mainScreen].bounds)) {
|
||||||
|
NSLog(@"in fullscreen");
|
||||||
|
} else NSLog(@"not fullscreen");
|
||||||
|
|
||||||
|
[self.reactViewController.view setFrame:[UIScreen mainScreen].bounds];
|
||||||
|
[self.reactViewController.view setNeedsLayout];
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -870,9 +868,15 @@ static int const RCTVideoUnset = -1;
|
|||||||
} else if([_ignoreSilentSwitch isEqualToString:@"obey"]) {
|
} else if([_ignoreSilentSwitch isEqualToString:@"obey"]) {
|
||||||
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryAmbient error:nil];
|
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryAmbient error:nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (@available(iOS 10.0, *) && !_automaticallyWaitsToMinimizeStalling) {
|
||||||
|
[_player playImmediatelyAtRate:_rate];
|
||||||
|
} else {
|
||||||
[_player play];
|
[_player play];
|
||||||
[_player setRate:_rate];
|
[_player setRate:_rate];
|
||||||
}
|
}
|
||||||
|
[_player setRate:_rate];
|
||||||
|
}
|
||||||
|
|
||||||
_paused = paused;
|
_paused = paused;
|
||||||
}
|
}
|
||||||
@ -957,11 +961,19 @@ static int const RCTVideoUnset = -1;
|
|||||||
_playerItem.preferredPeakBitRate = maxBitRate;
|
_playerItem.preferredPeakBitRate = maxBitRate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)setAutomaticallyWaitsToMinimizeStalling:(BOOL)waits
|
||||||
|
{
|
||||||
|
_automaticallyWaitsToMinimizeStalling = waits;
|
||||||
|
_player.automaticallyWaitsToMinimizeStalling = waits;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)applyModifiers
|
- (void)applyModifiers
|
||||||
{
|
{
|
||||||
if (_muted) {
|
if (_muted) {
|
||||||
|
if (!_controls) {
|
||||||
[_player setVolume:0];
|
[_player setVolume:0];
|
||||||
|
}
|
||||||
[_player setMuted:YES];
|
[_player setMuted:YES];
|
||||||
} else {
|
} else {
|
||||||
[_player setVolume:_volume];
|
[_player setVolume:_volume];
|
||||||
@ -1283,7 +1295,9 @@ static int const RCTVideoUnset = -1;
|
|||||||
{
|
{
|
||||||
if( _player )
|
if( _player )
|
||||||
{
|
{
|
||||||
|
if (!_playerViewController) {
|
||||||
_playerViewController = [self createPlayerViewController:_player withPlayerItem:_playerItem];
|
_playerViewController = [self createPlayerViewController:_player withPlayerItem:_playerItem];
|
||||||
|
}
|
||||||
// to prevent video from being animated when resizeMode is 'cover'
|
// to prevent video from being animated when resizeMode is 'cover'
|
||||||
// resize mode must be set before subview is added
|
// resize mode must be set before subview is added
|
||||||
[self setResizeMode:_resizeMode];
|
[self setResizeMode:_resizeMode];
|
||||||
@ -1294,6 +1308,8 @@ static int const RCTVideoUnset = -1;
|
|||||||
[self addSubview:_playerViewController.view];
|
[self addSubview:_playerViewController.view];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[_playerViewController addObserver:self forKeyPath:readyForDisplayKeyPath options:NSKeyValueObservingOptionNew context:nil];
|
||||||
|
|
||||||
[_playerViewController.contentOverlayView addObserver:self forKeyPath:@"frame" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:NULL];
|
[_playerViewController.contentOverlayView addObserver:self forKeyPath:@"frame" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:NULL];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1488,7 +1504,10 @@ static int const RCTVideoUnset = -1;
|
|||||||
[self removePlayerLayer];
|
[self removePlayerLayer];
|
||||||
|
|
||||||
[_playerViewController.contentOverlayView removeObserver:self forKeyPath:@"frame"];
|
[_playerViewController.contentOverlayView removeObserver:self forKeyPath:@"frame"];
|
||||||
|
[_playerViewController removeObserver:self forKeyPath:readyForDisplayKeyPath];
|
||||||
[_playerViewController.view removeFromSuperview];
|
[_playerViewController.view removeFromSuperview];
|
||||||
|
_playerViewController.rctDelegate = nil;
|
||||||
|
_playerViewController.player = nil;
|
||||||
_playerViewController = nil;
|
_playerViewController = nil;
|
||||||
|
|
||||||
[self removePlayerTimeObserver];
|
[self removePlayerTimeObserver];
|
||||||
|
@ -22,6 +22,7 @@ RCT_EXPORT_VIEW_PROPERTY(src, NSDictionary);
|
|||||||
RCT_EXPORT_VIEW_PROPERTY(maxBitRate, float);
|
RCT_EXPORT_VIEW_PROPERTY(maxBitRate, float);
|
||||||
RCT_EXPORT_VIEW_PROPERTY(resizeMode, NSString);
|
RCT_EXPORT_VIEW_PROPERTY(resizeMode, NSString);
|
||||||
RCT_EXPORT_VIEW_PROPERTY(repeat, BOOL);
|
RCT_EXPORT_VIEW_PROPERTY(repeat, BOOL);
|
||||||
|
RCT_EXPORT_VIEW_PROPERTY(automaticallyWaitsToMinimizeStalling, BOOL);
|
||||||
RCT_EXPORT_VIEW_PROPERTY(allowsExternalPlayback, BOOL);
|
RCT_EXPORT_VIEW_PROPERTY(allowsExternalPlayback, BOOL);
|
||||||
RCT_EXPORT_VIEW_PROPERTY(textTracks, NSArray);
|
RCT_EXPORT_VIEW_PROPERTY(textTracks, NSArray);
|
||||||
RCT_EXPORT_VIEW_PROPERTY(selectedTextTrack, NSDictionary);
|
RCT_EXPORT_VIEW_PROPERTY(selectedTextTrack, NSDictionary);
|
||||||
@ -45,25 +46,25 @@ RCT_EXPORT_VIEW_PROPERTY(filterEnabled, BOOL);
|
|||||||
RCT_EXPORT_VIEW_PROPERTY(progressUpdateInterval, float);
|
RCT_EXPORT_VIEW_PROPERTY(progressUpdateInterval, float);
|
||||||
RCT_EXPORT_VIEW_PROPERTY(restoreUserInterfaceForPIPStopCompletionHandler, BOOL);
|
RCT_EXPORT_VIEW_PROPERTY(restoreUserInterfaceForPIPStopCompletionHandler, BOOL);
|
||||||
/* Should support: onLoadStart, onLoad, and onError to stay consistent with Image */
|
/* Should support: onLoadStart, onLoad, and onError to stay consistent with Image */
|
||||||
RCT_EXPORT_VIEW_PROPERTY(onVideoLoadStart, RCTBubblingEventBlock);
|
RCT_EXPORT_VIEW_PROPERTY(onVideoLoadStart, RCTDirectEventBlock);
|
||||||
RCT_EXPORT_VIEW_PROPERTY(onVideoLoad, RCTBubblingEventBlock);
|
RCT_EXPORT_VIEW_PROPERTY(onVideoLoad, RCTDirectEventBlock);
|
||||||
RCT_EXPORT_VIEW_PROPERTY(onVideoBuffer, RCTBubblingEventBlock);
|
RCT_EXPORT_VIEW_PROPERTY(onVideoBuffer, RCTDirectEventBlock);
|
||||||
RCT_EXPORT_VIEW_PROPERTY(onVideoError, RCTBubblingEventBlock);
|
RCT_EXPORT_VIEW_PROPERTY(onVideoError, RCTDirectEventBlock);
|
||||||
RCT_EXPORT_VIEW_PROPERTY(onVideoProgress, RCTBubblingEventBlock);
|
RCT_EXPORT_VIEW_PROPERTY(onVideoProgress, RCTDirectEventBlock);
|
||||||
RCT_EXPORT_VIEW_PROPERTY(onBandwidthUpdate, RCTBubblingEventBlock);
|
RCT_EXPORT_VIEW_PROPERTY(onBandwidthUpdate, RCTDirectEventBlock);
|
||||||
RCT_EXPORT_VIEW_PROPERTY(onVideoSeek, RCTBubblingEventBlock);
|
RCT_EXPORT_VIEW_PROPERTY(onVideoSeek, RCTDirectEventBlock);
|
||||||
RCT_EXPORT_VIEW_PROPERTY(onVideoEnd, RCTBubblingEventBlock);
|
RCT_EXPORT_VIEW_PROPERTY(onVideoEnd, RCTDirectEventBlock);
|
||||||
RCT_EXPORT_VIEW_PROPERTY(onTimedMetadata, RCTBubblingEventBlock);
|
RCT_EXPORT_VIEW_PROPERTY(onTimedMetadata, RCTDirectEventBlock);
|
||||||
RCT_EXPORT_VIEW_PROPERTY(onVideoAudioBecomingNoisy, RCTBubblingEventBlock);
|
RCT_EXPORT_VIEW_PROPERTY(onVideoAudioBecomingNoisy, RCTDirectEventBlock);
|
||||||
RCT_EXPORT_VIEW_PROPERTY(onVideoFullscreenPlayerWillPresent, RCTBubblingEventBlock);
|
RCT_EXPORT_VIEW_PROPERTY(onVideoFullscreenPlayerWillPresent, RCTDirectEventBlock);
|
||||||
RCT_EXPORT_VIEW_PROPERTY(onVideoFullscreenPlayerDidPresent, RCTBubblingEventBlock);
|
RCT_EXPORT_VIEW_PROPERTY(onVideoFullscreenPlayerDidPresent, RCTDirectEventBlock);
|
||||||
RCT_EXPORT_VIEW_PROPERTY(onVideoFullscreenPlayerWillDismiss, RCTBubblingEventBlock);
|
RCT_EXPORT_VIEW_PROPERTY(onVideoFullscreenPlayerWillDismiss, RCTDirectEventBlock);
|
||||||
RCT_EXPORT_VIEW_PROPERTY(onVideoFullscreenPlayerDidDismiss, RCTBubblingEventBlock);
|
RCT_EXPORT_VIEW_PROPERTY(onVideoFullscreenPlayerDidDismiss, RCTDirectEventBlock);
|
||||||
RCT_EXPORT_VIEW_PROPERTY(onReadyForDisplay, RCTBubblingEventBlock);
|
RCT_EXPORT_VIEW_PROPERTY(onReadyForDisplay, RCTDirectEventBlock);
|
||||||
RCT_EXPORT_VIEW_PROPERTY(onPlaybackStalled, RCTBubblingEventBlock);
|
RCT_EXPORT_VIEW_PROPERTY(onPlaybackStalled, RCTDirectEventBlock);
|
||||||
RCT_EXPORT_VIEW_PROPERTY(onPlaybackResume, RCTBubblingEventBlock);
|
RCT_EXPORT_VIEW_PROPERTY(onPlaybackResume, RCTDirectEventBlock);
|
||||||
RCT_EXPORT_VIEW_PROPERTY(onPlaybackRateChange, RCTBubblingEventBlock);
|
RCT_EXPORT_VIEW_PROPERTY(onPlaybackRateChange, RCTDirectEventBlock);
|
||||||
RCT_EXPORT_VIEW_PROPERTY(onVideoExternalPlaybackChange, RCTBubblingEventBlock);
|
RCT_EXPORT_VIEW_PROPERTY(onVideoExternalPlaybackChange, RCTDirectEventBlock);
|
||||||
RCT_REMAP_METHOD(save,
|
RCT_REMAP_METHOD(save,
|
||||||
options:(NSDictionary *)options
|
options:(NSDictionary *)options
|
||||||
reactTag:(nonnull NSNumber *)reactTag
|
reactTag:(nonnull NSNumber *)reactTag
|
||||||
@ -79,8 +80,8 @@ RCT_REMAP_METHOD(save,
|
|||||||
}
|
}
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
RCT_EXPORT_VIEW_PROPERTY(onPictureInPictureStatusChanged, RCTBubblingEventBlock);
|
RCT_EXPORT_VIEW_PROPERTY(onPictureInPictureStatusChanged, RCTDirectEventBlock);
|
||||||
RCT_EXPORT_VIEW_PROPERTY(onRestoreUserInterfaceForPictureInPictureStop, RCTBubblingEventBlock);
|
RCT_EXPORT_VIEW_PROPERTY(onRestoreUserInterfaceForPictureInPictureStop, RCTDirectEventBlock);
|
||||||
|
|
||||||
- (NSDictionary *)constantsToExport
|
- (NSDictionary *)constantsToExport
|
||||||
{
|
{
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "react-native-video",
|
"name": "react-native-video",
|
||||||
"version": "4.4.1",
|
"version": "5.1.0-alpha1",
|
||||||
"description": "A <Video /> element for react-native",
|
"description": "A <Video /> element for react-native",
|
||||||
"main": "Video.js",
|
"main": "Video.js",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@ -45,11 +45,6 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "node_modules/.bin/eslint *.js"
|
"test": "node_modules/.bin/eslint *.js"
|
||||||
},
|
},
|
||||||
"rnpm": {
|
|
||||||
"android": {
|
|
||||||
"sourceDir": "./android-exoplayer"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"files": [
|
"files": [
|
||||||
"android-exoplayer",
|
"android-exoplayer",
|
||||||
"android",
|
"android",
|
||||||
|
Loading…
Reference in New Issue
Block a user