From 9764fe3be436b38ee369920934673a0512df3771 Mon Sep 17 00:00:00 2001 From: Hampton Maxwell Date: Sat, 2 Jun 2018 02:24:13 -0700 Subject: [PATCH 01/16] First pass at text track selection, mostly complete --- Video.js | 7 +++ .../exoplayer/ReactExoplayerView.java | 63 ++++++++++++++++++- .../exoplayer/ReactExoplayerViewManager.java | 14 +++++ ios/RCTVideo.m | 46 ++++++++++++++ ios/RCTVideoManager.m | 1 + 5 files changed, 130 insertions(+), 1 deletion(-) diff --git a/Video.js b/Video.js index aced6e55..849305ea 100644 --- a/Video.js +++ b/Video.js @@ -274,6 +274,13 @@ Video.propTypes = { poster: PropTypes.string, posterResizeMode: Image.propTypes.resizeMode, repeat: PropTypes.bool, + selectedTextTrack: PropTypes.shape({ + type: PropTypes.string.isRequired, + value: PropTypes.oneOfType([ + PropTypes.string, + PropTypes.number + ]) + }), paused: PropTypes.bool, muted: PropTypes.bool, volume: PropTypes.number, diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index aa60ac14..b16899c8 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -16,6 +16,7 @@ import android.widget.FrameLayout; import com.brentvatne.react.R; import com.brentvatne.receiver.AudioBecomingNoisyReceiver; import com.brentvatne.receiver.BecomingNoisyListener; +import com.facebook.react.bridge.Dynamic; import com.facebook.react.bridge.LifecycleEventListener; import com.facebook.react.uimanager.ThemedReactContext; import com.google.android.exoplayer2.C; @@ -45,6 +46,7 @@ import com.google.android.exoplayer2.source.smoothstreaming.DefaultSsChunkSource import com.google.android.exoplayer2.source.smoothstreaming.SsMediaSource; import com.google.android.exoplayer2.trackselection.AdaptiveTrackSelection; 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.TrackSelection; import com.google.android.exoplayer2.trackselection.TrackSelectionArray; @@ -535,7 +537,8 @@ class ReactExoplayerView extends FrameLayout implements decoderInitializationException.decoderName); } } - } else if (e.type == ExoPlaybackException.TYPE_SOURCE) { + } + else if (e.type == ExoPlaybackException.TYPE_SOURCE) { ex = e.getSourceException(); errorString = getResources().getString(R.string.unrecognized_media_format); } @@ -565,6 +568,16 @@ class ReactExoplayerView extends FrameLayout implements return false; } + public int getTextTrackRendererIndex() { + int rendererCount = player.getRendererCount(); + for (int rendererIndex = 0; rendererIndex < rendererCount; rendererIndex++) { + if (player.getRendererType(rendererIndex) == C.TRACK_TYPE_TEXT) { + return rendererIndex; + } + } + return C.INDEX_UNSET; + } + @Override public void onMetadata(Metadata metadata) { eventEmitter.timedMetadata(metadata); @@ -626,6 +639,54 @@ class ReactExoplayerView extends FrameLayout implements this.repeat = repeat; } + public void setSelectedTextTrack(String type, Dynamic value) { + int index = getTextTrackRendererIndex(); + if (index == C.INDEX_UNSET) { + return; + } + MappingTrackSelector.MappedTrackInfo info = trackSelector.getCurrentMappedTrackInfo(); + if (info == null) { + return; + } + + TrackGroupArray groups = info.getTrackGroups(index); + int trackIndex = C.INDEX_UNSET; + if (TextUtils.isEmpty(type)) { + // Do nothing + } else if (type.equals("language")) { + for (int i = 0; i < groups.length; ++i) { + Format format = groups.get(i).getFormat(0); + if (format.language != null && format.language.equals(value.asString())) { + trackIndex = i; + break; + } + } + } else if (type.equals("title")) { + for (int i = 0; i < groups.length; ++i) { + Format format = groups.get(i).getFormat(0); + if (format.id != null && format.id.equals(value.asString())) { + trackIndex = i; + break; + } + } + } else if (type.equals("index")) { + trackIndex = value.asInt(); + } else { // default. invalid type or "system" + trackSelector.clearSelectionOverrides(index); + return; + } + + if (trackIndex == C.INDEX_UNSET) { + trackSelector.clearSelectionOverrides(trackIndex); + return; + } + + MappingTrackSelector.SelectionOverride override + = new MappingTrackSelector.SelectionOverride( + new FixedTrackSelection.Factory(), trackIndex, 0); + trackSelector.setSelectionOverride(index, groups, override); + } + public void setPausedModifier(boolean paused) { isPaused = paused; if (player != null) { diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java index b500f400..1f42d5ca 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java @@ -4,6 +4,7 @@ import android.content.Context; import android.net.Uri; import android.text.TextUtils; +import com.facebook.react.bridge.Dynamic; import com.facebook.react.bridge.ReadableMap; import com.facebook.react.common.MapBuilder; import com.facebook.react.uimanager.ThemedReactContext; @@ -24,6 +25,9 @@ public class ReactExoplayerViewManager extends ViewGroupManager index) { + option = [group.options objectAtIndex:index]; + } + } + } else { // default. invalid type or "system" + // _player.appliesMediaSelectionCriteriaAutomatically = YES; + return; + } + + // If a match isn't found, option will be nil and text tracks will be disabled + // _player.appliesMediaSelectionCriteriaAutomatically = NO; + [_player.currentItem selectMediaOption:option inMediaSelectionGroup:group]; +} + - (BOOL)getFullscreen { return _fullscreenPlayerPresented; diff --git a/ios/RCTVideoManager.m b/ios/RCTVideoManager.m index c1edb9b7..f187502f 100644 --- a/ios/RCTVideoManager.m +++ b/ios/RCTVideoManager.m @@ -22,6 +22,7 @@ RCT_EXPORT_MODULE(); RCT_EXPORT_VIEW_PROPERTY(src, NSDictionary); RCT_EXPORT_VIEW_PROPERTY(resizeMode, NSString); RCT_EXPORT_VIEW_PROPERTY(repeat, BOOL); +RCT_EXPORT_VIEW_PROPERTY(selectedTextTrack, NSDictionary); RCT_EXPORT_VIEW_PROPERTY(paused, BOOL); RCT_EXPORT_VIEW_PROPERTY(muted, BOOL); RCT_EXPORT_VIEW_PROPERTY(controls, BOOL); From b44ae2c0c299d90507ef684e6cf5123063515467 Mon Sep 17 00:00:00 2001 From: Hampton Maxwell Date: Sat, 2 Jun 2018 19:41:25 -0700 Subject: [PATCH 02/16] Get automatic system track selection working --- ios/RCTVideo.m | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ios/RCTVideo.m b/ios/RCTVideo.m index 15464162..94189e27 100644 --- a/ios/RCTVideo.m +++ b/ios/RCTVideo.m @@ -640,11 +640,10 @@ static NSString *const timedMetadata = @"timedMetadata"; - (void)setSelectedTextTrack:(NSDictionary *)selectedTextTrack { _selectedTextTrack = selectedTextTrack; NSString *type = selectedTextTrack[@"type"]; - AVMediaSelectionGroup *group = [_player.currentItem.asset mediaSelectionGroupForMediaCharacteristic:AVMediaCharacteristicLegible]; - AVMediaSelectionOption *option; + if ([type isEqualToString:@"disabled"]) { // Do nothing. We want to ensure option is nil } else if ([type isEqualToString:@"language"] || [type isEqualToString:@"title"]) { @@ -664,6 +663,8 @@ static NSString *const timedMetadata = @"timedMetadata"; break; } } + //} else if ([type isEqualToString:@"default"]) { + // option = group.defaultOption; */ } else if ([type isEqualToString:@"index"]) { if ([selectedTextTrack[@"value"] isKindOfClass:[NSNumber class]]) { int index = [selectedTextTrack[@"value"] intValue]; @@ -672,12 +673,11 @@ static NSString *const timedMetadata = @"timedMetadata"; } } } else { // default. invalid type or "system" - // _player.appliesMediaSelectionCriteriaAutomatically = YES; + [_player.currentItem selectMediaOptionAutomaticallyInMediaSelectionGroup:group]; return; } // If a match isn't found, option will be nil and text tracks will be disabled - // _player.appliesMediaSelectionCriteriaAutomatically = NO; [_player.currentItem selectMediaOption:option inMediaSelectionGroup:group]; } From 4d008e56f2ec7feaa07e0ad41245545b53018ab7 Mon Sep 17 00:00:00 2001 From: Hampton Maxwell Date: Sat, 2 Jun 2018 19:41:50 -0700 Subject: [PATCH 03/16] Add disabled text track selection option --- .../java/com/brentvatne/exoplayer/ReactExoplayerView.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index b16899c8..5f1585ed 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -5,12 +5,14 @@ import android.app.Activity; import android.content.Context; import android.media.AudioManager; import android.net.Uri; +import android.os.Build; import android.os.Handler; import android.os.Message; import android.text.TextUtils; import android.util.Log; import android.view.View; import android.view.Window; +import android.view.accessibility.CaptioningManager; import android.widget.FrameLayout; import com.brentvatne.react.R; @@ -59,6 +61,7 @@ import java.net.CookieManager; import java.net.CookiePolicy; import java.lang.Math; import java.lang.Object; +import java.util.Locale; @SuppressLint("ViewConstructor") class ReactExoplayerView extends FrameLayout implements @@ -653,6 +656,9 @@ class ReactExoplayerView extends FrameLayout implements int trackIndex = C.INDEX_UNSET; if (TextUtils.isEmpty(type)) { // Do nothing + } else if (type.equals("disabled")) { + trackSelector.setSelectionOverride(index, groups, null); + return; } else if (type.equals("language")) { for (int i = 0; i < groups.length; ++i) { Format format = groups.get(i).getFormat(0); From 11584f28d1374e49b44b1b1fcc2d5ce0e7b5c0eb Mon Sep 17 00:00:00 2001 From: Hampton Maxwell Date: Mon, 4 Jun 2018 11:48:59 -0700 Subject: [PATCH 04/16] Apply initial text track prop when the video is loaded --- .../java/com/brentvatne/exoplayer/ReactExoplayerView.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index 5f1585ed..a6e88409 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -104,6 +104,8 @@ class ReactExoplayerView extends FrameLayout implements private Uri srcUri; private String extension; private boolean repeat; + private String textTrackType; + private Dynamic textTrackValue; private boolean disableFocus; private float mProgressUpdateInterval = 250.0f; private boolean playInBackground = false; @@ -449,6 +451,7 @@ class ReactExoplayerView extends FrameLayout implements private void videoLoaded() { if (loadVideoStarted) { loadVideoStarted = false; + setSelectedTextTrack(textTrackType, textTrackValue); Format videoFormat = player.getVideoFormat(); int width = videoFormat != null ? videoFormat.width : 0; int height = videoFormat != null ? videoFormat.height : 0; @@ -643,6 +646,9 @@ class ReactExoplayerView extends FrameLayout implements } public void setSelectedTextTrack(String type, Dynamic value) { + textTrackType = type; + textTrackValue = value; + int index = getTextTrackRendererIndex(); if (index == C.INDEX_UNSET) { return; From 4f5effaacae04ca0e122c5b8333fa83198445d2a Mon Sep 17 00:00:00 2001 From: Hampton Maxwell Date: Tue, 5 Jun 2018 15:55:33 -0700 Subject: [PATCH 05/16] Basic bug report template --- .github/ISSUE_TEMPLATE/bug_report.md | 31 ++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000..778a4f02 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,31 @@ +--- +name: Bug report +about: Create a report to help us improve + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**Platform:** +Which player are you using? +iOS, Android ExoPlayer, Android MediaPlayer, Windows UWP, or Windows WPF + +**Video:** +If possible, provide a URL or download for the video that causes the issue. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Additional context** +Add any other context about the problem here. From a80bcbb83f6fa2212d749cbe6f7f77e2a29799df Mon Sep 17 00:00:00 2001 From: Hampton Maxwell Date: Tue, 5 Jun 2018 15:56:54 -0700 Subject: [PATCH 06/16] Update issue templates --- .github/ISSUE_TEMPLATE/bug_report.md | 31 ---------------------------- .github/ISSUE_TEMPLATE/custom.md | 7 +++++++ 2 files changed, 7 insertions(+), 31 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/custom.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index 778a4f02..00000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -name: Bug report -about: Create a report to help us improve - ---- - -**Describe the bug** -A clear and concise description of what the bug is. - -**Platform:** -Which player are you using? -iOS, Android ExoPlayer, Android MediaPlayer, Windows UWP, or Windows WPF - -**Video:** -If possible, provide a URL or download for the video that causes the issue. - -**To Reproduce** -Steps to reproduce the behavior: -1. Go to '...' -2. Click on '....' -3. Scroll down to '....' -4. See error - -**Expected behavior** -A clear and concise description of what you expected to happen. - -**Screenshots** -If applicable, add screenshots to help explain your problem. - -**Additional context** -Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/custom.md b/.github/ISSUE_TEMPLATE/custom.md new file mode 100644 index 00000000..befe3586 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/custom.md @@ -0,0 +1,7 @@ +--- +name: Custom issue template +about: Describe this issue template's purpose here. + +--- + +Test From 792d617bbad9a9ba47d5b610ab2f908e19333040 Mon Sep 17 00:00:00 2001 From: Hampton Maxwell Date: Tue, 5 Jun 2018 16:02:04 -0700 Subject: [PATCH 07/16] Create ISSUE_TEMPLATE.md --- .github/ISSUE_TEMPLATE.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE.md diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 00000000..b0312b30 --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,19 @@ +### Current behavior +Describe what happens when you encounter this issue. + +### Reproduction steps +A 1, 2, 3, etc. list of what's needed to see the issue happen. + +### Expected behavior +Describe what you wanted to happen + +### Platform +Which player are you experiencing the problem on: +* iOS +* Android ExoPlayer +* Android MediaPlayer +* Windows UWP +* Windows WPF + +### Video sample +If possible, include a link to the video that has the problem that can be streamed or downloaded from. From 586cd70f68960e5faf5612e6f2d945dc88e5431e Mon Sep 17 00:00:00 2001 From: Hampton Maxwell Date: Tue, 5 Jun 2018 16:02:17 -0700 Subject: [PATCH 08/16] Delete custom.md --- .github/ISSUE_TEMPLATE/custom.md | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/custom.md diff --git a/.github/ISSUE_TEMPLATE/custom.md b/.github/ISSUE_TEMPLATE/custom.md deleted file mode 100644 index befe3586..00000000 --- a/.github/ISSUE_TEMPLATE/custom.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -name: Custom issue template -about: Describe this issue template's purpose here. - ---- - -Test From c097c7d38558ea035c27ac091ed7b30b7971643a Mon Sep 17 00:00:00 2001 From: Hampton Maxwell Date: Tue, 5 Jun 2018 17:27:19 -0700 Subject: [PATCH 09/16] Begin expanding props docs --- README.md | 82 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/README.md b/README.md index 312e5ae0..78e72fa9 100644 --- a/README.md +++ b/README.md @@ -229,6 +229,88 @@ var styles = StyleSheet.create({ }, }); ``` + +### Props +* [ignoreSilentSwitch](#ignoreSilentSwitch) +* [muted](#muted) +* [paused](#paused) +* [progressUpdateInterval](#progressUpdateInterval) +* [rate](#rate) +* [resizeMode](#resizeMode) + +#### ignoreSilentSwitch +Controls the iOS silent switch behavior +* **"ignore"** - Play audio even if the silent switch is set +* **"inherit"** - Use the default AVPlayer behavior +* **"obey"** - Don't play audio if the silent switch is set + +Type | Default | Platforms +--- | --- | --- +string | "inherit" | iOS + +#### muted +If true, mutes the audio + +Type | Default | Platforms +--- | --- | --- +boolean | false | all + +#### paused +If true, pauses the media + +Type | Default | Platforms +--- | --- | --- +boolean | false | all + +#### progressUpdateInterval +Delay between onProgress events in milliseconds + +Type | Default | Platforms +--- | --- | --- +number | 250.0 | all + +#### rate +Speed at which the media should play. +* **0.0** - Pauses the video +* **1.0** - Play at normal speed +* **Other values** - Slow down or speed up playback + +Type | Default | Platforms +--- | --- | --- +number | 1.0 | all + +Note: On Android MediaPlayer, rate is only supported on Android 6.0 and higher devices. + +#### resizeMode +Determines how to resize the video when the frame doesn't match the raw video dimensions. +* **"contain"** - Scale the image uniformly (maintain the image's aspect ratio) so that both dimensions (width and height) of the image will be equal to or less than the corresponding dimension of the view (minus padding). +* **"cover"** - Scale the image uniformly (maintain the image's aspect ratio) so that both dimensions (width and height) of the image will be equal to or larger than the corresponding dimension of the view (minus padding). +* **"none"** - Don't apply resize +* **"stretch"** - Scale width and height independently, This may change the aspect ratio of the src. + +Type | Default | Platforms +--- | --- | --- +string | "none" | Android ExoPlayer, Android MediaPlayer, iOS, Windows UWP + +#### volume +Adjust the volume. +* **0.0** - Mute the audio +* **Other values** - Reduce volume +* **1.0** - Play at full volume + + + +Name | Type | Default | Platforms | Description +--- | --- | --- | --- | --- +ignoreSilentSwitch | string | "obey" | iOS | **"ignore"** Play audio even if the silent switch is set
**inherit** - Use the default behavior
**"obey"** - Don't play audio if the silent switch is set +muted | boolean | false | all | When true, mutes the audio +paused | boolean | false | all | When true, pauses the video +progressUpdateInterval | number | 250.0 | all | Delay between onProgress events in milliseconds +rate | number | 1.0 | all | normal rate to speed up or slow down playback
Note: For Android MediaPlayer, only works on Android 6.0+ +repeat | boolean | false | all | When true, once the end is reached the video will replay from the start +resizeMode | string | "none" | AMP
Exo
iOS
UWP | **"contain"** - Scale the image uniformly (maintain the image's aspect ratio) so that both dimensions (width and height) of the image will be equal to or less than the corresponding dimension of the view (minus padding).
**"cover"** - Scale the image uniformly (maintain the image's aspect ratio) so that both dimensions (width and height) of the image will be equal to or larger than the corresponding dimension of the view (minus padding).
"none" Don't apply resize
**"stretch"** - Scale width and height independently, This may change the aspect ratio of the src.
+volume | number | 1.0 | all | + To see the full list of available props, you can check the [propTypes](https://github.com/react-native-community/react-native-video/blob/master/Video.js#L246) of the Video.js component. - By default, iOS 9+ will only load encrypted HTTPS urls. If you need to load content from a webserver that only supports HTTP, you will need to modify your Info.plist file and add the following entry: From 87c1b87a391a8a984a05ac2e34d03f064f3ebebd Mon Sep 17 00:00:00 2001 From: Hampton Maxwell Date: Tue, 5 Jun 2018 18:03:14 -0700 Subject: [PATCH 10/16] Add docs for most configurable props Add individual sections for each of the common configurable props --- README.md | 95 ++++++++++++++++++++++++------------------------------- 1 file changed, 41 insertions(+), 54 deletions(-) diff --git a/README.md b/README.md index 78e72fa9..6d248cb0 100644 --- a/README.md +++ b/README.md @@ -181,21 +181,11 @@ using System.Collections.Generic; // on a single screen if you like.