Merge branch 'master' into master
This commit is contained in:
commit
7460df9edb
35
README.md
35
README.md
@ -229,7 +229,8 @@ var styles = StyleSheet.create({
|
|||||||
* [progressUpdateInterval](#progressupdateinterval)
|
* [progressUpdateInterval](#progressupdateinterval)
|
||||||
* [rate](#rate)
|
* [rate](#rate)
|
||||||
* [repeat](#repeat)
|
* [repeat](#repeat)
|
||||||
* [resizeMode](#resize-mode)
|
* [resizeMode](#resizemode)
|
||||||
|
* [selectedTextTrack](#selectedtexttrack)
|
||||||
* [volume](#volume)
|
* [volume](#volume)
|
||||||
|
|
||||||
#### ignoreSilentSwitch
|
#### ignoreSilentSwitch
|
||||||
@ -304,6 +305,38 @@ Determines how to resize the video when the frame doesn't match the raw video di
|
|||||||
|
|
||||||
Platforms: Android ExoPlayer, Android MediaPlayer, iOS, Windows UWP
|
Platforms: Android ExoPlayer, Android MediaPlayer, iOS, Windows UWP
|
||||||
|
|
||||||
|
#### selectedTextTrack
|
||||||
|
Configure which text track (caption or subtitle), if any, is shown.
|
||||||
|
|
||||||
|
```
|
||||||
|
selectedTextTrack={{
|
||||||
|
type: Type,
|
||||||
|
value: Value
|
||||||
|
}}
|
||||||
|
```
|
||||||
|
|
||||||
|
Example:
|
||||||
|
```
|
||||||
|
selectedTextTrack={{
|
||||||
|
type: "title",
|
||||||
|
value: "English Subtitles"
|
||||||
|
}}
|
||||||
|
```
|
||||||
|
|
||||||
|
Type | Value | Description
|
||||||
|
--- | --- | ---
|
||||||
|
"system" (default) | N/A | Display captions only if the system preference for captions is enabled
|
||||||
|
"disabled" | N/A | Don't display a text track
|
||||||
|
"title" | string | Display the text track with the title specified as the Value, e.g. "French 1"
|
||||||
|
"language" | string | Display the text track with the language specified as the Value, e.g. "fr"
|
||||||
|
"index" | number | Display the text track with the index specified as the value, e.g. 0
|
||||||
|
|
||||||
|
Both iOS & Android offer Settings to enable Captions for hearing impaired people. If "system" is selected and the Captions Setting is enabled, iOS/Android will look for a caption that matches that customer's language and display it.
|
||||||
|
|
||||||
|
If a track matching the specified Type (and Value if appropriate) is unavailable, no text track will be displayed. If multiple tracks match the criteria, the first match will be used.
|
||||||
|
|
||||||
|
Platforms: Android ExoPlayer, iOS
|
||||||
|
|
||||||
#### volume
|
#### volume
|
||||||
Adjust the volume.
|
Adjust the volume.
|
||||||
* **1.0 (default)** - Play at full volume
|
* **1.0 (default)** - Play at full volume
|
||||||
|
7
Video.js
7
Video.js
@ -275,6 +275,13 @@ Video.propTypes = {
|
|||||||
posterResizeMode: Image.propTypes.resizeMode,
|
posterResizeMode: Image.propTypes.resizeMode,
|
||||||
repeat: PropTypes.bool,
|
repeat: PropTypes.bool,
|
||||||
allowsExternalPlayback: PropTypes.bool,
|
allowsExternalPlayback: PropTypes.bool,
|
||||||
|
selectedTextTrack: PropTypes.shape({
|
||||||
|
type: PropTypes.string.isRequired,
|
||||||
|
value: PropTypes.oneOfType([
|
||||||
|
PropTypes.string,
|
||||||
|
PropTypes.number
|
||||||
|
])
|
||||||
|
}),
|
||||||
paused: PropTypes.bool,
|
paused: PropTypes.bool,
|
||||||
muted: PropTypes.bool,
|
muted: PropTypes.bool,
|
||||||
volume: PropTypes.number,
|
volume: PropTypes.number,
|
||||||
|
@ -5,17 +5,20 @@ import android.app.Activity;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.media.AudioManager;
|
import android.media.AudioManager;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.os.Build;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.Window;
|
import android.view.Window;
|
||||||
|
import android.view.accessibility.CaptioningManager;
|
||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
|
|
||||||
import com.brentvatne.react.R;
|
import com.brentvatne.react.R;
|
||||||
import com.brentvatne.receiver.AudioBecomingNoisyReceiver;
|
import com.brentvatne.receiver.AudioBecomingNoisyReceiver;
|
||||||
import com.brentvatne.receiver.BecomingNoisyListener;
|
import com.brentvatne.receiver.BecomingNoisyListener;
|
||||||
|
import com.facebook.react.bridge.Dynamic;
|
||||||
import com.facebook.react.bridge.LifecycleEventListener;
|
import com.facebook.react.bridge.LifecycleEventListener;
|
||||||
import com.facebook.react.uimanager.ThemedReactContext;
|
import com.facebook.react.uimanager.ThemedReactContext;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
@ -45,6 +48,7 @@ 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;
|
||||||
@ -57,6 +61,7 @@ import java.net.CookieManager;
|
|||||||
import java.net.CookiePolicy;
|
import java.net.CookiePolicy;
|
||||||
import java.lang.Math;
|
import java.lang.Math;
|
||||||
import java.lang.Object;
|
import java.lang.Object;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
@SuppressLint("ViewConstructor")
|
@SuppressLint("ViewConstructor")
|
||||||
class ReactExoplayerView extends FrameLayout implements
|
class ReactExoplayerView extends FrameLayout implements
|
||||||
@ -99,6 +104,8 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
private Uri srcUri;
|
private Uri srcUri;
|
||||||
private String extension;
|
private String extension;
|
||||||
private boolean repeat;
|
private boolean repeat;
|
||||||
|
private String textTrackType;
|
||||||
|
private Dynamic textTrackValue;
|
||||||
private boolean disableFocus;
|
private boolean disableFocus;
|
||||||
private float mProgressUpdateInterval = 250.0f;
|
private float mProgressUpdateInterval = 250.0f;
|
||||||
private boolean playInBackground = false;
|
private boolean playInBackground = false;
|
||||||
@ -444,6 +451,7 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
private void videoLoaded() {
|
private void videoLoaded() {
|
||||||
if (loadVideoStarted) {
|
if (loadVideoStarted) {
|
||||||
loadVideoStarted = false;
|
loadVideoStarted = false;
|
||||||
|
setSelectedTextTrack(textTrackType, textTrackValue);
|
||||||
Format videoFormat = player.getVideoFormat();
|
Format videoFormat = player.getVideoFormat();
|
||||||
int width = videoFormat != null ? videoFormat.width : 0;
|
int width = videoFormat != null ? videoFormat.width : 0;
|
||||||
int height = videoFormat != null ? videoFormat.height : 0;
|
int height = videoFormat != null ? videoFormat.height : 0;
|
||||||
@ -535,7 +543,8 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
decoderInitializationException.decoderName);
|
decoderInitializationException.decoderName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (e.type == ExoPlaybackException.TYPE_SOURCE) {
|
}
|
||||||
|
else if (e.type == ExoPlaybackException.TYPE_SOURCE) {
|
||||||
ex = e.getSourceException();
|
ex = e.getSourceException();
|
||||||
errorString = getResources().getString(R.string.unrecognized_media_format);
|
errorString = getResources().getString(R.string.unrecognized_media_format);
|
||||||
}
|
}
|
||||||
@ -565,6 +574,16 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
return false;
|
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
|
@Override
|
||||||
public void onMetadata(Metadata metadata) {
|
public void onMetadata(Metadata metadata) {
|
||||||
eventEmitter.timedMetadata(metadata);
|
eventEmitter.timedMetadata(metadata);
|
||||||
@ -626,6 +645,60 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
this.repeat = repeat;
|
this.repeat = repeat;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setSelectedTextTrack(String type, Dynamic value) {
|
||||||
|
textTrackType = type;
|
||||||
|
textTrackValue = 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("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);
|
||||||
|
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) {
|
public void setPausedModifier(boolean paused) {
|
||||||
isPaused = paused;
|
isPaused = paused;
|
||||||
if (player != null) {
|
if (player != null) {
|
||||||
|
@ -4,6 +4,7 @@ import android.content.Context;
|
|||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
|
||||||
|
import com.facebook.react.bridge.Dynamic;
|
||||||
import com.facebook.react.bridge.ReadableMap;
|
import com.facebook.react.bridge.ReadableMap;
|
||||||
import com.facebook.react.common.MapBuilder;
|
import com.facebook.react.common.MapBuilder;
|
||||||
import com.facebook.react.uimanager.ThemedReactContext;
|
import com.facebook.react.uimanager.ThemedReactContext;
|
||||||
@ -24,6 +25,9 @@ public class ReactExoplayerViewManager extends ViewGroupManager<ReactExoplayerVi
|
|||||||
private static final String PROP_SRC_TYPE = "type";
|
private static final String PROP_SRC_TYPE = "type";
|
||||||
private static final String PROP_RESIZE_MODE = "resizeMode";
|
private static final String PROP_RESIZE_MODE = "resizeMode";
|
||||||
private static final String PROP_REPEAT = "repeat";
|
private static final String PROP_REPEAT = "repeat";
|
||||||
|
private static final String PROP_SELECTED_TEXT_TRACK = "selectedTextTrack";
|
||||||
|
private static final String PROP_SELECTED_TEXT_TRACK_TYPE = "type";
|
||||||
|
private static final String PROP_SELECTED_TEXT_TRACK_VALUE = "value";
|
||||||
private static final String PROP_PAUSED = "paused";
|
private static final String PROP_PAUSED = "paused";
|
||||||
private static final String PROP_MUTED = "muted";
|
private static final String PROP_MUTED = "muted";
|
||||||
private static final String PROP_VOLUME = "volume";
|
private static final String PROP_VOLUME = "volume";
|
||||||
@ -116,6 +120,16 @@ public class ReactExoplayerViewManager extends ViewGroupManager<ReactExoplayerVi
|
|||||||
videoView.setRepeatModifier(repeat);
|
videoView.setRepeatModifier(repeat);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ReactProp(name = PROP_SELECTED_TEXT_TRACK)
|
||||||
|
public void setSelectedTextTrack(final ReactExoplayerView videoView,
|
||||||
|
@Nullable ReadableMap selectedTextTrack) {
|
||||||
|
String typeString = selectedTextTrack.hasKey(PROP_SELECTED_TEXT_TRACK_TYPE)
|
||||||
|
? selectedTextTrack.getString(PROP_SELECTED_TEXT_TRACK_TYPE) : null;
|
||||||
|
Dynamic value = selectedTextTrack.hasKey(PROP_SELECTED_TEXT_TRACK_VALUE)
|
||||||
|
? selectedTextTrack.getDynamic(PROP_SELECTED_TEXT_TRACK_VALUE) : null;
|
||||||
|
videoView.setSelectedTextTrack(typeString, value);
|
||||||
|
}
|
||||||
|
|
||||||
@ReactProp(name = PROP_PAUSED, defaultBoolean = false)
|
@ReactProp(name = PROP_PAUSED, defaultBoolean = false)
|
||||||
public void setPaused(final ReactExoplayerView videoView, final boolean paused) {
|
public void setPaused(final ReactExoplayerView videoView, final boolean paused) {
|
||||||
videoView.setPausedModifier(paused);
|
videoView.setPausedModifier(paused);
|
||||||
|
@ -41,6 +41,7 @@ static NSString *const timedMetadata = @"timedMetadata";
|
|||||||
BOOL _paused;
|
BOOL _paused;
|
||||||
BOOL _repeat;
|
BOOL _repeat;
|
||||||
BOOL _allowsExternalPlayback;
|
BOOL _allowsExternalPlayback;
|
||||||
|
NSDictionary * _selectedTextTrack;
|
||||||
BOOL _playbackStalled;
|
BOOL _playbackStalled;
|
||||||
BOOL _playInBackground;
|
BOOL _playInBackground;
|
||||||
BOOL _playWhenInactive;
|
BOOL _playWhenInactive;
|
||||||
@ -637,6 +638,7 @@ static NSString *const timedMetadata = @"timedMetadata";
|
|||||||
[_player setMuted:NO];
|
[_player setMuted:NO];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[self setSelectedTextTrack:_selectedTextTrack];
|
||||||
[self setResizeMode:_resizeMode];
|
[self setResizeMode:_resizeMode];
|
||||||
[self setRepeat:_repeat];
|
[self setRepeat:_repeat];
|
||||||
[self setPaused:_paused];
|
[self setPaused:_paused];
|
||||||
@ -648,6 +650,50 @@ static NSString *const timedMetadata = @"timedMetadata";
|
|||||||
_repeat = repeat;
|
_repeat = repeat;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (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"]) {
|
||||||
|
NSString *value = selectedTextTrack[@"value"];
|
||||||
|
for (int i = 0; i < group.options.count; ++i) {
|
||||||
|
AVMediaSelectionOption *currentOption = [group.options objectAtIndex:i];
|
||||||
|
NSString *optionValue;
|
||||||
|
if ([type isEqualToString:@"language"]) {
|
||||||
|
optionValue = [currentOption extendedLanguageTag];
|
||||||
|
} else {
|
||||||
|
optionValue = [[[currentOption commonMetadata]
|
||||||
|
valueForKey:@"value"]
|
||||||
|
objectAtIndex:0];
|
||||||
|
}
|
||||||
|
if ([value isEqualToString:optionValue]) {
|
||||||
|
option = currentOption;
|
||||||
|
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];
|
||||||
|
if (group.options.count > index) {
|
||||||
|
option = [group.options objectAtIndex:index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else { // default. invalid type or "system"
|
||||||
|
[_player.currentItem selectMediaOptionAutomaticallyInMediaSelectionGroup:group];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If a match isn't found, option will be nil and text tracks will be disabled
|
||||||
|
[_player.currentItem selectMediaOption:option inMediaSelectionGroup:group];
|
||||||
|
}
|
||||||
|
|
||||||
- (BOOL)getFullscreen
|
- (BOOL)getFullscreen
|
||||||
{
|
{
|
||||||
return _fullscreenPlayerPresented;
|
return _fullscreenPlayerPresented;
|
||||||
|
@ -23,6 +23,7 @@ RCT_EXPORT_VIEW_PROPERTY(src, NSDictionary);
|
|||||||
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(allowsExternalPlayback, BOOL);
|
RCT_EXPORT_VIEW_PROPERTY(allowsExternalPlayback, BOOL);
|
||||||
|
RCT_EXPORT_VIEW_PROPERTY(selectedTextTrack, NSDictionary);
|
||||||
RCT_EXPORT_VIEW_PROPERTY(paused, BOOL);
|
RCT_EXPORT_VIEW_PROPERTY(paused, BOOL);
|
||||||
RCT_EXPORT_VIEW_PROPERTY(muted, BOOL);
|
RCT_EXPORT_VIEW_PROPERTY(muted, BOOL);
|
||||||
RCT_EXPORT_VIEW_PROPERTY(controls, BOOL);
|
RCT_EXPORT_VIEW_PROPERTY(controls, BOOL);
|
||||||
|
Loading…
Reference in New Issue
Block a user