Merge pull request #1199 from sridhard/master
Youtube like video track selection
This commit is contained in:
commit
04f3f40278
17
Video.js
17
Video.js
@ -106,6 +106,12 @@ export default class Video extends Component {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
_onBandwidthUpdate = (event) => {
|
||||||
|
if (this.props.onBandwidthUpdate) {
|
||||||
|
this.props.onBandwidthUpdate(event.nativeEvent);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
_onSeek = (event) => {
|
_onSeek = (event) => {
|
||||||
if (this.state.showPoster && !this.props.audioOnly) {
|
if (this.state.showPoster && !this.props.audioOnly) {
|
||||||
this.setState({showPoster: false});
|
this.setState({showPoster: false});
|
||||||
@ -247,6 +253,7 @@ export default class Video extends Component {
|
|||||||
onVideoSeek: this._onSeek,
|
onVideoSeek: this._onSeek,
|
||||||
onVideoEnd: this._onEnd,
|
onVideoEnd: this._onEnd,
|
||||||
onVideoBuffer: this._onBuffer,
|
onVideoBuffer: this._onBuffer,
|
||||||
|
onVideoBandwidthUpdate: this._onBandwidthUpdate,
|
||||||
onTimedMetadata: this._onTimedMetadata,
|
onTimedMetadata: this._onTimedMetadata,
|
||||||
onVideoAudioBecomingNoisy: this._onAudioBecomingNoisy,
|
onVideoAudioBecomingNoisy: this._onAudioBecomingNoisy,
|
||||||
onVideoExternalPlaybackChange: this._onExternalPlaybackChange,
|
onVideoExternalPlaybackChange: this._onExternalPlaybackChange,
|
||||||
@ -313,6 +320,7 @@ Video.propTypes = {
|
|||||||
onVideoBuffer: PropTypes.func,
|
onVideoBuffer: PropTypes.func,
|
||||||
onVideoError: PropTypes.func,
|
onVideoError: PropTypes.func,
|
||||||
onVideoProgress: PropTypes.func,
|
onVideoProgress: PropTypes.func,
|
||||||
|
onVideoBandwidthUpdate: PropTypes.func,
|
||||||
onVideoSeek: PropTypes.func,
|
onVideoSeek: PropTypes.func,
|
||||||
onVideoEnd: PropTypes.func,
|
onVideoEnd: PropTypes.func,
|
||||||
onTimedMetadata: PropTypes.func,
|
onTimedMetadata: PropTypes.func,
|
||||||
@ -344,6 +352,13 @@ Video.propTypes = {
|
|||||||
PropTypes.number
|
PropTypes.number
|
||||||
])
|
])
|
||||||
}),
|
}),
|
||||||
|
selectedVideoTrack: PropTypes.shape({
|
||||||
|
type: PropTypes.string.isRequired,
|
||||||
|
value: PropTypes.oneOfType([
|
||||||
|
PropTypes.string,
|
||||||
|
PropTypes.number
|
||||||
|
])
|
||||||
|
}),
|
||||||
selectedTextTrack: PropTypes.shape({
|
selectedTextTrack: PropTypes.shape({
|
||||||
type: PropTypes.string.isRequired,
|
type: PropTypes.string.isRequired,
|
||||||
value: PropTypes.oneOfType([
|
value: PropTypes.oneOfType([
|
||||||
@ -377,6 +392,7 @@ Video.propTypes = {
|
|||||||
playInBackground: PropTypes.bool,
|
playInBackground: PropTypes.bool,
|
||||||
playWhenInactive: PropTypes.bool,
|
playWhenInactive: PropTypes.bool,
|
||||||
ignoreSilentSwitch: PropTypes.oneOf(['ignore', 'obey']),
|
ignoreSilentSwitch: PropTypes.oneOf(['ignore', 'obey']),
|
||||||
|
reportBandwidth: PropTypes.bool,
|
||||||
disableFocus: PropTypes.bool,
|
disableFocus: PropTypes.bool,
|
||||||
controls: PropTypes.bool,
|
controls: PropTypes.bool,
|
||||||
audioOnly: PropTypes.bool,
|
audioOnly: PropTypes.bool,
|
||||||
@ -391,6 +407,7 @@ Video.propTypes = {
|
|||||||
onBuffer: PropTypes.func,
|
onBuffer: PropTypes.func,
|
||||||
onError: PropTypes.func,
|
onError: PropTypes.func,
|
||||||
onProgress: PropTypes.func,
|
onProgress: PropTypes.func,
|
||||||
|
onBandwidthUpdate: PropTypes.func,
|
||||||
onSeek: PropTypes.func,
|
onSeek: PropTypes.func,
|
||||||
onEnd: PropTypes.func,
|
onEnd: PropTypes.func,
|
||||||
onFullscreenPlayerWillPresent: PropTypes.func,
|
onFullscreenPlayerWillPresent: PropTypes.func,
|
||||||
|
@ -21,6 +21,12 @@ dependencies {
|
|||||||
implementation('com.google.android.exoplayer:exoplayer:2.9.3') {
|
implementation('com.google.android.exoplayer:exoplayer:2.9.3') {
|
||||||
exclude group: 'com.android.support'
|
exclude group: 'com.android.support'
|
||||||
}
|
}
|
||||||
|
implementation project(':exoplayer-library-core')
|
||||||
|
implementation project(':exoplayer-library-dash')
|
||||||
|
implementation project(':exoplayer-library-ui')
|
||||||
|
implementation project(':exoplayer-library-smoothstreaming')
|
||||||
|
implementation project(':exoplayer-library-hls')
|
||||||
|
implementation project(':exoplayer-extension-okhttp')
|
||||||
|
|
||||||
// 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 "com.android.support:support-annotations:${safeExtGet('supportLibVersion', '+')}"
|
||||||
|
@ -46,6 +46,7 @@ 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.SingleSampleMediaSource;
|
import com.google.android.exoplayer2.source.SingleSampleMediaSource;
|
||||||
import com.google.android.exoplayer2.source.TrackGroupArray;
|
import com.google.android.exoplayer2.source.TrackGroupArray;
|
||||||
|
import com.google.android.exoplayer2.source.TrackGroup;
|
||||||
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;
|
||||||
@ -59,6 +60,7 @@ 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.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.MimeTypes;
|
||||||
import com.google.android.exoplayer2.util.Util;
|
import com.google.android.exoplayer2.util.Util;
|
||||||
@ -76,6 +78,7 @@ import java.util.Locale;
|
|||||||
class ReactExoplayerView extends FrameLayout implements
|
class ReactExoplayerView extends FrameLayout implements
|
||||||
LifecycleEventListener,
|
LifecycleEventListener,
|
||||||
ExoPlayer.EventListener,
|
ExoPlayer.EventListener,
|
||||||
|
BandwidthMeter.EventListener,
|
||||||
BecomingNoisyListener,
|
BecomingNoisyListener,
|
||||||
AudioManager.OnAudioFocusChangeListener,
|
AudioManager.OnAudioFocusChangeListener,
|
||||||
MetadataRenderer.Output {
|
MetadataRenderer.Output {
|
||||||
@ -85,6 +88,7 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
private static final DefaultBandwidthMeter BANDWIDTH_METER = new DefaultBandwidthMeter();
|
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();
|
||||||
@ -92,7 +96,7 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
private final VideoEventEmitter eventEmitter;
|
private final VideoEventEmitter eventEmitter;
|
||||||
|
|
||||||
private Handler mainHandler;
|
private Handler mainHandler;
|
||||||
private ExoPlayerView exoPlayerView;
|
private ExoPlayerView exoPlayerView;
|
||||||
|
|
||||||
@ -124,6 +128,8 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
private boolean repeat;
|
private boolean repeat;
|
||||||
private String audioTrackType;
|
private String audioTrackType;
|
||||||
private Dynamic audioTrackValue;
|
private Dynamic audioTrackValue;
|
||||||
|
private String videoTrackType;
|
||||||
|
private Dynamic videoTrackValue;
|
||||||
private ReadableArray audioTracks;
|
private ReadableArray audioTracks;
|
||||||
private String textTrackType;
|
private String textTrackType;
|
||||||
private Dynamic textTrackValue;
|
private Dynamic textTrackValue;
|
||||||
@ -132,6 +138,7 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
private float mProgressUpdateInterval = 250.0f;
|
private float mProgressUpdateInterval = 250.0f;
|
||||||
private boolean playInBackground = false;
|
private boolean playInBackground = false;
|
||||||
private Map<String, String> requestHeaders;
|
private Map<String, String> requestHeaders;
|
||||||
|
private boolean mReportBandwidth = false;
|
||||||
// \ End props
|
// \ End props
|
||||||
|
|
||||||
// React
|
// React
|
||||||
@ -162,8 +169,11 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
public ReactExoplayerView(ThemedReactContext context) {
|
public ReactExoplayerView(ThemedReactContext context) {
|
||||||
super(context);
|
super(context);
|
||||||
this.themedReactContext = context;
|
this.themedReactContext = context;
|
||||||
createViews();
|
|
||||||
this.eventEmitter = new VideoEventEmitter(context);
|
this.eventEmitter = new VideoEventEmitter(context);
|
||||||
|
|
||||||
|
createViews();
|
||||||
|
|
||||||
audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
|
audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
|
||||||
themedReactContext.addLifecycleEventListener(this);
|
themedReactContext.addLifecycleEventListener(this);
|
||||||
audioBecomingNoisyReceiver = new AudioBecomingNoisyReceiver(themedReactContext);
|
audioBecomingNoisyReceiver = new AudioBecomingNoisyReceiver(themedReactContext);
|
||||||
@ -238,9 +248,15 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
stopPlayback();
|
stopPlayback();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//BandwidthMeter.EventListener implementation
|
||||||
|
@Override
|
||||||
|
public void onBandwidthSample(int elapsedMs, long bytes, long bitrate) {
|
||||||
|
if (mReportBandwidth) {
|
||||||
|
eventEmitter.bandwidthReport(bitrate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Internal methods
|
// Internal methods
|
||||||
|
|
||||||
private void initializePlayer() {
|
private void initializePlayer() {
|
||||||
if (player == null) {
|
if (player == null) {
|
||||||
TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory(BANDWIDTH_METER);
|
TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory(BANDWIDTH_METER);
|
||||||
@ -255,6 +271,7 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
player.setMetadataOutput(this);
|
player.setMetadataOutput(this);
|
||||||
exoPlayerView.setPlayer(player);
|
exoPlayerView.setPlayer(player);
|
||||||
audioBecomingNoisyReceiver.setListener(this);
|
audioBecomingNoisyReceiver.setListener(this);
|
||||||
|
BANDWIDTH_METER.addEventListener(new Handler(), this);
|
||||||
setPlayWhenReady(!isPaused);
|
setPlayWhenReady(!isPaused);
|
||||||
playerNeedsSource = true;
|
playerNeedsSource = true;
|
||||||
|
|
||||||
@ -345,6 +362,7 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
progressHandler.removeMessages(SHOW_PROGRESS);
|
progressHandler.removeMessages(SHOW_PROGRESS);
|
||||||
themedReactContext.removeLifecycleEventListener(this);
|
themedReactContext.removeLifecycleEventListener(this);
|
||||||
audioBecomingNoisyReceiver.removeListener();
|
audioBecomingNoisyReceiver.removeListener();
|
||||||
|
BANDWIDTH_METER.removeEventListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean requestAudioFocus() {
|
private boolean requestAudioFocus() {
|
||||||
@ -520,12 +538,13 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
if (loadVideoStarted) {
|
if (loadVideoStarted) {
|
||||||
loadVideoStarted = false;
|
loadVideoStarted = false;
|
||||||
setSelectedAudioTrack(audioTrackType, audioTrackValue);
|
setSelectedAudioTrack(audioTrackType, audioTrackValue);
|
||||||
|
setSelectedVideoTrack(videoTrackType, videoTrackValue);
|
||||||
setSelectedTextTrack(textTrackType, textTrackValue);
|
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;
|
||||||
eventEmitter.load(player.getDuration(), player.getCurrentPosition(), width, height,
|
eventEmitter.load(player.getDuration(), player.getCurrentPosition(), width, height,
|
||||||
getAudioTrackInfo(), getTextTrackInfo());
|
getAudioTrackInfo(), getTextTrackInfo(), getVideoTrackInfo());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -546,10 +565,39 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
audioTrack.putString("title", format.id != null ? format.id : "");
|
audioTrack.putString("title", format.id != null ? format.id : "");
|
||||||
audioTrack.putString("type", format.sampleMimeType);
|
audioTrack.putString("type", format.sampleMimeType);
|
||||||
audioTrack.putString("language", format.language != null ? format.language : "");
|
audioTrack.putString("language", format.language != null ? format.language : "");
|
||||||
|
audioTrack.putString("bitrate", format.bitrate == Format.NO_VALUE ? ""
|
||||||
|
: String.format(Locale.US, "%.2fMbps", format.bitrate / 1000000f));
|
||||||
audioTracks.pushMap(audioTrack);
|
audioTracks.pushMap(audioTrack);
|
||||||
}
|
}
|
||||||
return audioTracks;
|
return audioTracks;
|
||||||
}
|
}
|
||||||
|
private WritableArray getVideoTrackInfo() {
|
||||||
|
WritableArray videoTracks = Arguments.createArray();
|
||||||
|
|
||||||
|
MappingTrackSelector.MappedTrackInfo info = trackSelector.getCurrentMappedTrackInfo();
|
||||||
|
int index = getTrackRendererIndex(C.TRACK_TYPE_VIDEO);
|
||||||
|
if (info == null || index == C.INDEX_UNSET) {
|
||||||
|
return videoTracks;
|
||||||
|
}
|
||||||
|
|
||||||
|
TrackGroupArray groups = info.getTrackGroups(index);
|
||||||
|
for (int i = 0; i < groups.length; ++i) {
|
||||||
|
TrackGroup group = groups.get(i);
|
||||||
|
|
||||||
|
for (int trackIndex = 0; trackIndex < group.length; trackIndex++) {
|
||||||
|
Format format = group.getFormat(trackIndex);
|
||||||
|
WritableMap videoTrack = Arguments.createMap();
|
||||||
|
videoTrack.putInt("width", format.width == Format.NO_VALUE ? 0 : format.width);
|
||||||
|
videoTrack.putInt("height",format.height == Format.NO_VALUE ? 0 : format.height);
|
||||||
|
videoTrack.putInt("bitrate", format.bitrate == Format.NO_VALUE ? 0 : format.bitrate);
|
||||||
|
videoTrack.putString("codecs", format.codecs != null ? format.codecs : "");
|
||||||
|
videoTrack.putString("trackId",
|
||||||
|
format.id == null ? String.valueOf(trackIndex) : format.id);
|
||||||
|
videoTracks.pushMap(videoTrack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return videoTracks;
|
||||||
|
}
|
||||||
|
|
||||||
private WritableArray getTextTrackInfo() {
|
private WritableArray getTextTrackInfo() {
|
||||||
WritableArray textTracks = Arguments.createArray();
|
WritableArray textTracks = Arguments.createArray();
|
||||||
@ -726,6 +774,10 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
mProgressUpdateInterval = progressUpdateInterval;
|
mProgressUpdateInterval = progressUpdateInterval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setReportBandwidth(boolean reportBandwidth) {
|
||||||
|
mReportBandwidth = reportBandwidth;
|
||||||
|
}
|
||||||
|
|
||||||
public void setRawSrc(final Uri uri, final String extension) {
|
public void setRawSrc(final Uri uri, final String extension) {
|
||||||
if (uri != null) {
|
if (uri != null) {
|
||||||
boolean isOriginalSourceNull = srcUri == null;
|
boolean isOriginalSourceNull = srcUri == null;
|
||||||
@ -777,7 +829,8 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
TrackGroupArray groups = info.getTrackGroups(rendererIndex);
|
TrackGroupArray groups = info.getTrackGroups(rendererIndex);
|
||||||
int trackIndex = C.INDEX_UNSET;
|
int groupIndex = C.INDEX_UNSET;
|
||||||
|
int[] tracks = {0} ;
|
||||||
|
|
||||||
if (TextUtils.isEmpty(type)) {
|
if (TextUtils.isEmpty(type)) {
|
||||||
type = "default";
|
type = "default";
|
||||||
@ -795,7 +848,7 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
for (int i = 0; i < groups.length; ++i) {
|
for (int i = 0; i < groups.length; ++i) {
|
||||||
Format format = groups.get(i).getFormat(0);
|
Format format = groups.get(i).getFormat(0);
|
||||||
if (format.language != null && format.language.equals(value.asString())) {
|
if (format.language != null && format.language.equals(value.asString())) {
|
||||||
trackIndex = i;
|
groupIndex = i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -803,28 +856,46 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
for (int i = 0; i < groups.length; ++i) {
|
for (int i = 0; i < groups.length; ++i) {
|
||||||
Format format = groups.get(i).getFormat(0);
|
Format format = groups.get(i).getFormat(0);
|
||||||
if (format.id != null && format.id.equals(value.asString())) {
|
if (format.id != null && format.id.equals(value.asString())) {
|
||||||
trackIndex = i;
|
groupIndex = i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (type.equals("index")) {
|
} else if (type.equals("index")) {
|
||||||
if (value.asInt() < groups.length) {
|
if (value.asInt() < groups.length) {
|
||||||
trackIndex = value.asInt();
|
groupIndex = value.asInt();
|
||||||
}
|
}
|
||||||
} else { // default
|
} else if (type.equals("resolution")) {
|
||||||
if (rendererIndex == C.TRACK_TYPE_TEXT && Util.SDK_INT > 18 && groups.length > 0) {
|
int height = value.asInt();
|
||||||
// Use system settings if possible
|
for (int i = 0; i < groups.length; ++i) { // Search for the exact height
|
||||||
CaptioningManager captioningManager
|
TrackGroup group = groups.get(i);
|
||||||
= (CaptioningManager)themedReactContext.getSystemService(Context.CAPTIONING_SERVICE);
|
for (int j = 0; j < group.length; j++) {
|
||||||
if (captioningManager != null && captioningManager.isEnabled()) {
|
Format format = group.getFormat(j);
|
||||||
trackIndex = getTrackIndexForDefaultLocale(groups);
|
if (format.height == value.asInt()) {
|
||||||
|
groupIndex = i;
|
||||||
|
tracks[0] = j;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (rendererIndex == C.TRACK_TYPE_AUDIO) {
|
|
||||||
trackIndex = getTrackIndexForDefaultLocale(groups);
|
|
||||||
}
|
}
|
||||||
|
} else if (rendererIndex == C.TRACK_TYPE_TEXT && Util.SDK_INT > 18) { // Text default
|
||||||
|
// Use system settings if possible
|
||||||
|
CaptioningManager captioningManager
|
||||||
|
= (CaptioningManager)themedReactContext.getSystemService(Context.CAPTIONING_SERVICE);
|
||||||
|
if (captioningManager != null && captioningManager.isEnabled()) {
|
||||||
|
groupIndex = getGroupIndexForDefaultLocale(groups);
|
||||||
|
}
|
||||||
|
} else if (rendererIndex == C.TRACK_TYPE_AUDIO) { // Audio default
|
||||||
|
groupIndex = getGroupIndexForDefaultLocale(groups);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (trackIndex == C.INDEX_UNSET) {
|
if (groupIndex == C.INDEX_UNSET && trackType == C.TRACK_TYPE_VIDEO) { // Video auto
|
||||||
|
TrackGroup group = groups.get(0);
|
||||||
|
tracks = new int[group.length];
|
||||||
|
groupIndex = 0;
|
||||||
|
for (int j = 0; j < group.length; j++) {
|
||||||
|
tracks[j] = j;
|
||||||
|
}
|
||||||
|
} else if (groupIndex == C.INDEX_UNSET) {
|
||||||
trackSelector.setParameters(disableParameters);
|
trackSelector.setParameters(disableParameters);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -833,28 +904,34 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
.buildUpon()
|
.buildUpon()
|
||||||
.setRendererDisabled(rendererIndex, false)
|
.setRendererDisabled(rendererIndex, false)
|
||||||
.setSelectionOverride(rendererIndex, groups,
|
.setSelectionOverride(rendererIndex, groups,
|
||||||
new DefaultTrackSelector.SelectionOverride(trackIndex, 0))
|
new DefaultTrackSelector.SelectionOverride(groupIndex, tracks))
|
||||||
.build();
|
.build();
|
||||||
trackSelector.setParameters(selectionParameters);
|
trackSelector.setParameters(selectionParameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getTrackIndexForDefaultLocale(TrackGroupArray groups) {
|
private int getGroupIndexForDefaultLocale(TrackGroupArray groups) {
|
||||||
if (groups.length == 0) { // Avoid a crash if we try to select a non-existant group
|
if (groups.length == 0){
|
||||||
return C.INDEX_UNSET;
|
return C.INDEX_UNSET;
|
||||||
}
|
}
|
||||||
|
|
||||||
int trackIndex = 0; // default if no match
|
int groupIndex = 0; // default if no match
|
||||||
String locale2 = Locale.getDefault().getLanguage(); // 2 letter code
|
String locale2 = Locale.getDefault().getLanguage(); // 2 letter code
|
||||||
String locale3 = Locale.getDefault().getISO3Language(); // 3 letter code
|
String locale3 = Locale.getDefault().getISO3Language(); // 3 letter code
|
||||||
for (int i = 0; i < groups.length; ++i) {
|
for (int i = 0; i < groups.length; ++i) {
|
||||||
Format format = groups.get(i).getFormat(0);
|
Format format = groups.get(i).getFormat(0);
|
||||||
String language = format.language;
|
String language = format.language;
|
||||||
if (language != null && (language.equals(locale2) || language.equals(locale3))) {
|
if (language != null && (language.equals(locale2) || language.equals(locale3))) {
|
||||||
trackIndex = i;
|
groupIndex = i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return trackIndex;
|
return groupIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSelectedVideoTrack(String type, Dynamic value) {
|
||||||
|
videoTrackType = type;
|
||||||
|
videoTrackValue = value;
|
||||||
|
setSelectedTrack(C.TRACK_TYPE_VIDEO, videoTrackType, videoTrackValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSelectedAudioTrack(String type, Dynamic value) {
|
public void setSelectedAudioTrack(String type, Dynamic value) {
|
||||||
|
@ -45,6 +45,7 @@ public class ReactExoplayerViewManager extends ViewGroupManager<ReactExoplayerVi
|
|||||||
private static final String PROP_BUFFER_CONFIG_BUFFER_FOR_PLAYBACK_MS = "bufferForPlaybackMs";
|
private static final String PROP_BUFFER_CONFIG_BUFFER_FOR_PLAYBACK_MS = "bufferForPlaybackMs";
|
||||||
private static final String PROP_BUFFER_CONFIG_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS = "bufferForPlaybackAfterRebufferMs";
|
private static final String PROP_BUFFER_CONFIG_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS = "bufferForPlaybackAfterRebufferMs";
|
||||||
private static final String PROP_PROGRESS_UPDATE_INTERVAL = "progressUpdateInterval";
|
private static final String PROP_PROGRESS_UPDATE_INTERVAL = "progressUpdateInterval";
|
||||||
|
private static final String PROP_REPORT_BANDWIDTH = "reportBandwidth";
|
||||||
private static final String PROP_SEEK = "seek";
|
private static final String PROP_SEEK = "seek";
|
||||||
private static final String PROP_RATE = "rate";
|
private static final String PROP_RATE = "rate";
|
||||||
private static final String PROP_MAXIMUM_BIT_RATE = "maxBitRate";
|
private static final String PROP_MAXIMUM_BIT_RATE = "maxBitRate";
|
||||||
@ -52,6 +53,9 @@ public class ReactExoplayerViewManager extends ViewGroupManager<ReactExoplayerVi
|
|||||||
private static final String PROP_DISABLE_FOCUS = "disableFocus";
|
private static final String PROP_DISABLE_FOCUS = "disableFocus";
|
||||||
private static final String PROP_FULLSCREEN = "fullscreen";
|
private static final String PROP_FULLSCREEN = "fullscreen";
|
||||||
private static final String PROP_USE_TEXTURE_VIEW = "useTextureView";
|
private static final String PROP_USE_TEXTURE_VIEW = "useTextureView";
|
||||||
|
private static final String PROP_SELECTED_VIDEO_TRACK = "selectedVideoTrack";
|
||||||
|
private static final String PROP_SELECTED_VIDEO_TRACK_TYPE = "type";
|
||||||
|
private static final String PROP_SELECTED_VIDEO_TRACK_VALUE = "value";
|
||||||
private static final String PROP_HIDE_SHUTTER_VIEW = "hideShutterView";
|
private static final String PROP_HIDE_SHUTTER_VIEW = "hideShutterView";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -138,6 +142,20 @@ public class ReactExoplayerViewManager extends ViewGroupManager<ReactExoplayerVi
|
|||||||
videoView.setRepeatModifier(repeat);
|
videoView.setRepeatModifier(repeat);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ReactProp(name = PROP_SELECTED_VIDEO_TRACK)
|
||||||
|
public void setSelectedVideoTrack(final ReactExoplayerView videoView,
|
||||||
|
@Nullable ReadableMap selectedVideoTrack) {
|
||||||
|
String typeString = null;
|
||||||
|
Dynamic value = null;
|
||||||
|
if (selectedVideoTrack != null) {
|
||||||
|
typeString = selectedVideoTrack.hasKey(PROP_SELECTED_VIDEO_TRACK_TYPE)
|
||||||
|
? selectedVideoTrack.getString(PROP_SELECTED_VIDEO_TRACK_TYPE) : null;
|
||||||
|
value = selectedVideoTrack.hasKey(PROP_SELECTED_VIDEO_TRACK_VALUE)
|
||||||
|
? selectedVideoTrack.getDynamic(PROP_SELECTED_VIDEO_TRACK_VALUE) : null;
|
||||||
|
}
|
||||||
|
videoView.setSelectedVideoTrack(typeString, value);
|
||||||
|
}
|
||||||
|
|
||||||
@ReactProp(name = PROP_SELECTED_AUDIO_TRACK)
|
@ReactProp(name = PROP_SELECTED_AUDIO_TRACK)
|
||||||
public void setSelectedAudioTrack(final ReactExoplayerView videoView,
|
public void setSelectedAudioTrack(final ReactExoplayerView videoView,
|
||||||
@Nullable ReadableMap selectedAudioTrack) {
|
@Nullable ReadableMap selectedAudioTrack) {
|
||||||
@ -192,6 +210,11 @@ public class ReactExoplayerViewManager extends ViewGroupManager<ReactExoplayerVi
|
|||||||
videoView.setProgressUpdateInterval(progressUpdateInterval);
|
videoView.setProgressUpdateInterval(progressUpdateInterval);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ReactProp(name = PROP_REPORT_BANDWIDTH, defaultBoolean = false)
|
||||||
|
public void setReportBandwidth(final ReactExoplayerView videoView, final boolean reportBandwidth) {
|
||||||
|
videoView.setReportBandwidth(reportBandwidth);
|
||||||
|
}
|
||||||
|
|
||||||
@ReactProp(name = PROP_SEEK)
|
@ReactProp(name = PROP_SEEK)
|
||||||
public void setSeek(final ReactExoplayerView videoView, final float seek) {
|
public void setSeek(final ReactExoplayerView videoView, final float seek) {
|
||||||
videoView.seekTo(Math.round(seek * 1000f));
|
videoView.seekTo(Math.round(seek * 1000f));
|
||||||
|
@ -29,6 +29,7 @@ class VideoEventEmitter {
|
|||||||
private static final String EVENT_LOAD = "onVideoLoad";
|
private static final String EVENT_LOAD = "onVideoLoad";
|
||||||
private static final String EVENT_ERROR = "onVideoError";
|
private static final String EVENT_ERROR = "onVideoError";
|
||||||
private static final String EVENT_PROGRESS = "onVideoProgress";
|
private static final String EVENT_PROGRESS = "onVideoProgress";
|
||||||
|
private static final String EVENT_BANDWIDTH = "onVideoBandwidthUpdate";
|
||||||
private static final String EVENT_SEEK = "onVideoSeek";
|
private static final String EVENT_SEEK = "onVideoSeek";
|
||||||
private static final String EVENT_END = "onVideoEnd";
|
private static final String EVENT_END = "onVideoEnd";
|
||||||
private static final String EVENT_FULLSCREEN_WILL_PRESENT = "onVideoFullscreenPlayerWillPresent";
|
private static final String EVENT_FULLSCREEN_WILL_PRESENT = "onVideoFullscreenPlayerWillPresent";
|
||||||
@ -66,6 +67,7 @@ class VideoEventEmitter {
|
|||||||
EVENT_AUDIO_BECOMING_NOISY,
|
EVENT_AUDIO_BECOMING_NOISY,
|
||||||
EVENT_AUDIO_FOCUS_CHANGE,
|
EVENT_AUDIO_FOCUS_CHANGE,
|
||||||
EVENT_PLAYBACK_RATE_CHANGE,
|
EVENT_PLAYBACK_RATE_CHANGE,
|
||||||
|
EVENT_BANDWIDTH,
|
||||||
};
|
};
|
||||||
|
|
||||||
@Retention(RetentionPolicy.SOURCE)
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
@ -89,6 +91,7 @@ class VideoEventEmitter {
|
|||||||
EVENT_AUDIO_BECOMING_NOISY,
|
EVENT_AUDIO_BECOMING_NOISY,
|
||||||
EVENT_AUDIO_FOCUS_CHANGE,
|
EVENT_AUDIO_FOCUS_CHANGE,
|
||||||
EVENT_PLAYBACK_RATE_CHANGE,
|
EVENT_PLAYBACK_RATE_CHANGE,
|
||||||
|
EVENT_BANDWIDTH,
|
||||||
})
|
})
|
||||||
@interface VideoEvents {
|
@interface VideoEvents {
|
||||||
}
|
}
|
||||||
@ -109,6 +112,7 @@ class VideoEventEmitter {
|
|||||||
private static final String EVENT_PROP_WIDTH = "width";
|
private static final String EVENT_PROP_WIDTH = "width";
|
||||||
private static final String EVENT_PROP_HEIGHT = "height";
|
private static final String EVENT_PROP_HEIGHT = "height";
|
||||||
private static final String EVENT_PROP_ORIENTATION = "orientation";
|
private static final String EVENT_PROP_ORIENTATION = "orientation";
|
||||||
|
private static final String EVENT_PROP_VIDEO_TRACKS = "videoTracks";
|
||||||
private static final String EVENT_PROP_AUDIO_TRACKS = "audioTracks";
|
private static final String EVENT_PROP_AUDIO_TRACKS = "audioTracks";
|
||||||
private static final String EVENT_PROP_TEXT_TRACKS = "textTracks";
|
private static final String EVENT_PROP_TEXT_TRACKS = "textTracks";
|
||||||
private static final String EVENT_PROP_HAS_AUDIO_FOCUS = "hasAudioFocus";
|
private static final String EVENT_PROP_HAS_AUDIO_FOCUS = "hasAudioFocus";
|
||||||
@ -117,10 +121,12 @@ class VideoEventEmitter {
|
|||||||
|
|
||||||
private static final String EVENT_PROP_ERROR = "error";
|
private static final String EVENT_PROP_ERROR = "error";
|
||||||
private static final String EVENT_PROP_ERROR_STRING = "errorString";
|
private static final String EVENT_PROP_ERROR_STRING = "errorString";
|
||||||
private static final String EVENT_PROP_ERROR_EXCEPTION = "";
|
private static final String EVENT_PROP_ERROR_EXCEPTION = "errorException";
|
||||||
|
|
||||||
private static final String EVENT_PROP_TIMED_METADATA = "metadata";
|
private static final String EVENT_PROP_TIMED_METADATA = "metadata";
|
||||||
|
|
||||||
|
private static final String EVENT_PROP_BITRATE = "bitrate";
|
||||||
|
|
||||||
|
|
||||||
void setViewId(int viewId) {
|
void setViewId(int viewId) {
|
||||||
this.viewId = viewId;
|
this.viewId = viewId;
|
||||||
@ -131,7 +137,7 @@ class VideoEventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void load(double duration, double currentPosition, int videoWidth, int videoHeight,
|
void load(double duration, double currentPosition, int videoWidth, int videoHeight,
|
||||||
WritableArray audioTracks, WritableArray textTracks) {
|
WritableArray audioTracks, WritableArray textTracks, WritableArray videoTracks) {
|
||||||
WritableMap event = Arguments.createMap();
|
WritableMap event = Arguments.createMap();
|
||||||
event.putDouble(EVENT_PROP_DURATION, duration / 1000D);
|
event.putDouble(EVENT_PROP_DURATION, duration / 1000D);
|
||||||
event.putDouble(EVENT_PROP_CURRENT_TIME, currentPosition / 1000D);
|
event.putDouble(EVENT_PROP_CURRENT_TIME, currentPosition / 1000D);
|
||||||
@ -146,6 +152,7 @@ class VideoEventEmitter {
|
|||||||
}
|
}
|
||||||
event.putMap(EVENT_PROP_NATURAL_SIZE, naturalSize);
|
event.putMap(EVENT_PROP_NATURAL_SIZE, naturalSize);
|
||||||
|
|
||||||
|
event.putArray(EVENT_PROP_VIDEO_TRACKS, videoTracks);
|
||||||
event.putArray(EVENT_PROP_AUDIO_TRACKS, audioTracks);
|
event.putArray(EVENT_PROP_AUDIO_TRACKS, audioTracks);
|
||||||
event.putArray(EVENT_PROP_TEXT_TRACKS, textTracks);
|
event.putArray(EVENT_PROP_TEXT_TRACKS, textTracks);
|
||||||
|
|
||||||
@ -169,6 +176,12 @@ class VideoEventEmitter {
|
|||||||
receiveEvent(EVENT_PROGRESS, event);
|
receiveEvent(EVENT_PROGRESS, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void bandwidthReport(double bitRateEstimate) {
|
||||||
|
WritableMap event = Arguments.createMap();
|
||||||
|
event.putDouble(EVENT_PROP_BITRATE, bitRate);
|
||||||
|
receiveEvent(EVENT_BANDWIDTH, event);
|
||||||
|
}
|
||||||
|
|
||||||
void seek(long currentPosition, long seekTime) {
|
void seek(long currentPosition, long seekTime) {
|
||||||
WritableMap event = Arguments.createMap();
|
WritableMap event = Arguments.createMap();
|
||||||
event.putDouble(EVENT_PROP_CURRENT_TIME, currentPosition / 1000D);
|
event.putDouble(EVENT_PROP_CURRENT_TIME, currentPosition / 1000D);
|
||||||
|
@ -17,10 +17,11 @@ public class ReactVideoPackage implements ReactPackage {
|
|||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deprecated RN 0.47
|
// Deprecated RN 0.47
|
||||||
public List<Class<? extends JavaScriptModule>> createJSModules() {
|
public List<Class<? extends JavaScriptModule>> createJSModules() {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
|
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
@property (nonatomic, copy) RCTBubblingEventBlock onVideoBuffer;
|
@property (nonatomic, copy) RCTBubblingEventBlock onVideoBuffer;
|
||||||
@property (nonatomic, copy) RCTBubblingEventBlock onVideoError;
|
@property (nonatomic, copy) RCTBubblingEventBlock onVideoError;
|
||||||
@property (nonatomic, copy) RCTBubblingEventBlock onVideoProgress;
|
@property (nonatomic, copy) RCTBubblingEventBlock onVideoProgress;
|
||||||
|
@property (nonatomic, copy) RCTBubblingEventBlock onBandwidthUpdate;
|
||||||
@property (nonatomic, copy) RCTBubblingEventBlock onVideoSeek;
|
@property (nonatomic, copy) RCTBubblingEventBlock onVideoSeek;
|
||||||
@property (nonatomic, copy) RCTBubblingEventBlock onVideoEnd;
|
@property (nonatomic, copy) RCTBubblingEventBlock onVideoEnd;
|
||||||
@property (nonatomic, copy) RCTBubblingEventBlock onTimedMetadata;
|
@property (nonatomic, copy) RCTBubblingEventBlock onTimedMetadata;
|
||||||
|
@ -704,6 +704,24 @@ static int const RCTVideoUnset = -1;
|
|||||||
selector:@selector(playbackStalled:)
|
selector:@selector(playbackStalled:)
|
||||||
name:AVPlayerItemPlaybackStalledNotification
|
name:AVPlayerItemPlaybackStalledNotification
|
||||||
object:nil];
|
object:nil];
|
||||||
|
|
||||||
|
[[NSNotificationCenter defaultCenter] removeObserver:self
|
||||||
|
name:AVPlayerItemNewAccessLogEntryNotification
|
||||||
|
object:nil];
|
||||||
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||||
|
selector:@selector(handleAVPlayerAccess:)
|
||||||
|
name:AVPlayerItemNewAccessLogEntryNotification
|
||||||
|
object:nil];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)handleAVPlayerAccess:(NSNotification *)notification {
|
||||||
|
AVPlayerItemAccessLog *accessLog = [((AVPlayerItem *)notification.object) accessLog];
|
||||||
|
AVPlayerItemAccessLogEvent *lastEvent = accessLog.events.lastObject;
|
||||||
|
|
||||||
|
if (self.onBandwidthUpdate) {
|
||||||
|
self.onBandwidthUpdate(@{@"bitrate": [NSNumber numberWithFloat:lastEvent.observedBitrate]});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)playbackStalled:(NSNotification *)notification
|
- (void)playbackStalled:(NSNotification *)notification
|
||||||
|
@ -48,6 +48,7 @@ RCT_EXPORT_VIEW_PROPERTY(onVideoLoad, RCTBubblingEventBlock);
|
|||||||
RCT_EXPORT_VIEW_PROPERTY(onVideoBuffer, RCTBubblingEventBlock);
|
RCT_EXPORT_VIEW_PROPERTY(onVideoBuffer, RCTBubblingEventBlock);
|
||||||
RCT_EXPORT_VIEW_PROPERTY(onVideoError, RCTBubblingEventBlock);
|
RCT_EXPORT_VIEW_PROPERTY(onVideoError, RCTBubblingEventBlock);
|
||||||
RCT_EXPORT_VIEW_PROPERTY(onVideoProgress, RCTBubblingEventBlock);
|
RCT_EXPORT_VIEW_PROPERTY(onVideoProgress, RCTBubblingEventBlock);
|
||||||
|
RCT_EXPORT_VIEW_PROPERTY(onBandwidthUpdate, RCTBubblingEventBlock);
|
||||||
RCT_EXPORT_VIEW_PROPERTY(onVideoSeek, RCTBubblingEventBlock);
|
RCT_EXPORT_VIEW_PROPERTY(onVideoSeek, RCTBubblingEventBlock);
|
||||||
RCT_EXPORT_VIEW_PROPERTY(onVideoEnd, RCTBubblingEventBlock);
|
RCT_EXPORT_VIEW_PROPERTY(onVideoEnd, RCTBubblingEventBlock);
|
||||||
RCT_EXPORT_VIEW_PROPERTY(onTimedMetadata, RCTBubblingEventBlock);
|
RCT_EXPORT_VIEW_PROPERTY(onTimedMetadata, RCTBubblingEventBlock);
|
||||||
|
Loading…
Reference in New Issue
Block a user