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)
|
||||
* [rate](#rate)
|
||||
* [repeat](#repeat)
|
||||
* [resizeMode](#resize-mode)
|
||||
* [resizeMode](#resizemode)
|
||||
* [selectedTextTrack](#selectedtexttrack)
|
||||
* [volume](#volume)
|
||||
|
||||
#### 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
|
||||
|
||||
#### 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
|
||||
Adjust the volume.
|
||||
* **1.0 (default)** - Play at full volume
|
||||
|
7
Video.js
7
Video.js
@ -275,6 +275,13 @@ Video.propTypes = {
|
||||
posterResizeMode: Image.propTypes.resizeMode,
|
||||
repeat: PropTypes.bool,
|
||||
allowsExternalPlayback: 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,
|
||||
|
@ -5,17 +5,20 @@ 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;
|
||||
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 +48,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;
|
||||
@ -57,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
|
||||
@ -99,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;
|
||||
@ -444,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;
|
||||
@ -535,7 +543,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 +574,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 +645,60 @@ class ReactExoplayerView extends FrameLayout implements
|
||||
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) {
|
||||
isPaused = paused;
|
||||
if (player != null) {
|
||||
|
@ -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<ReactExoplayerVi
|
||||
private static final String PROP_SRC_TYPE = "type";
|
||||
private static final String PROP_RESIZE_MODE = "resizeMode";
|
||||
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_MUTED = "muted";
|
||||
private static final String PROP_VOLUME = "volume";
|
||||
@ -116,6 +120,16 @@ public class ReactExoplayerViewManager extends ViewGroupManager<ReactExoplayerVi
|
||||
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)
|
||||
public void setPaused(final ReactExoplayerView videoView, final boolean paused) {
|
||||
videoView.setPausedModifier(paused);
|
||||
|
@ -41,6 +41,7 @@ static NSString *const timedMetadata = @"timedMetadata";
|
||||
BOOL _paused;
|
||||
BOOL _repeat;
|
||||
BOOL _allowsExternalPlayback;
|
||||
NSDictionary * _selectedTextTrack;
|
||||
BOOL _playbackStalled;
|
||||
BOOL _playInBackground;
|
||||
BOOL _playWhenInactive;
|
||||
@ -637,6 +638,7 @@ static NSString *const timedMetadata = @"timedMetadata";
|
||||
[_player setMuted:NO];
|
||||
}
|
||||
|
||||
[self setSelectedTextTrack:_selectedTextTrack];
|
||||
[self setResizeMode:_resizeMode];
|
||||
[self setRepeat:_repeat];
|
||||
[self setPaused:_paused];
|
||||
@ -648,6 +650,50 @@ static NSString *const timedMetadata = @"timedMetadata";
|
||||
_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
|
||||
{
|
||||
return _fullscreenPlayerPresented;
|
||||
|
@ -23,6 +23,7 @@ RCT_EXPORT_VIEW_PROPERTY(src, NSDictionary);
|
||||
RCT_EXPORT_VIEW_PROPERTY(resizeMode, NSString);
|
||||
RCT_EXPORT_VIEW_PROPERTY(repeat, BOOL);
|
||||
RCT_EXPORT_VIEW_PROPERTY(allowsExternalPlayback, 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);
|
||||
|
Loading…
Reference in New Issue
Block a user