From 16688cef0f7e9acb1801bf6611329f1172453f90 Mon Sep 17 00:00:00 2001 From: sridhar Date: Fri, 24 Aug 2018 15:33:46 +0530 Subject: [PATCH] Youtube like video track selection 1. Loaded event will give the information about available video tracks 2. selectedVideoTrack prop will give the interface to select the video track. This prop will take the height of the video (Similar to youtube like 144p, 350p etc) and set the video track. If height is 0 then all video tracks will be added to list so that it will work like 'Auto' in youtube --- Video.js | 7 ++ .../exoplayer/ReactExoplayerView.java | 115 ++++++++++++++++-- .../exoplayer/ReactExoplayerViewManager.java | 17 +++ .../exoplayer/VideoEventEmitter.java | 4 +- .../brentvatne/react/ReactVideoPackage.java | 5 - 5 files changed, 129 insertions(+), 19 deletions(-) diff --git a/Video.js b/Video.js index 5c6cd1d3..a10c7dd4 100644 --- a/Video.js +++ b/Video.js @@ -323,6 +323,13 @@ Video.propTypes = { PropTypes.number ]) }), + selectedVideoTrack: PropTypes.shape({ + type: PropTypes.string.isRequired, + value: PropTypes.oneOfType([ + PropTypes.string, + PropTypes.number + ]) + }), selectedTextTrack: PropTypes.shape({ type: PropTypes.string.isRequired, value: PropTypes.oneOfType([ 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 e098c72a..bcac252f 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -46,6 +46,7 @@ import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.MergingMediaSource; 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.dash.DashMediaSource; import com.google.android.exoplayer2.source.dash.DefaultDashChunkSource; import com.google.android.exoplayer2.source.hls.HlsMediaSource; @@ -121,6 +122,8 @@ class ReactExoplayerView extends FrameLayout implements private boolean repeat; private String audioTrackType; private Dynamic audioTrackValue; + private String videoTrackType; + private Dynamic videoTrackValue; private ReadableArray audioTracks; private String textTrackType; private Dynamic textTrackValue; @@ -512,12 +515,13 @@ class ReactExoplayerView extends FrameLayout implements if (loadVideoStarted) { loadVideoStarted = false; setSelectedAudioTrack(audioTrackType, audioTrackValue); + setSelectedVideoTrack(videoTrackType, videoTrackValue); setSelectedTextTrack(textTrackType, textTrackValue); Format videoFormat = player.getVideoFormat(); int width = videoFormat != null ? videoFormat.width : 0; int height = videoFormat != null ? videoFormat.height : 0; eventEmitter.load(player.getDuration(), player.getCurrentPosition(), width, height, - getAudioTrackInfo(), getTextTrackInfo()); + getAudioTrackInfo(), getTextTrackInfo(), getVideoTrackInfo()); } } @@ -543,6 +547,40 @@ class ReactExoplayerView extends FrameLayout implements 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.putString("width", + format.width == Format.NO_VALUE ? "" : format.width + ""); + videoTrack.putString("height", + format.height == Format.NO_VALUE ? "" : format.height + ""); + videoTrack.putString("bitrate", + format.bitrate == Format.NO_VALUE ? "" + : String.format(Locale.US, "%.2fMbps", format.bitrate / 1000000f)); + videoTrack.putString("trackid", format.id == null ? "" : "" + format.id + ""); + + videoTracks.pushMap(videoTrack); + } + + } + return videoTracks; + } + private WritableArray getTextTrackInfo() { WritableArray textTracks = Arguments.createArray(); @@ -758,6 +796,7 @@ class ReactExoplayerView extends FrameLayout implements } public void setSelectedTrack(int trackType, String type, Dynamic value) { + int rendererIndex = getTrackRendererIndex(trackType); if (rendererIndex == C.INDEX_UNSET) { return; @@ -768,7 +807,8 @@ class ReactExoplayerView extends FrameLayout implements } TrackGroupArray groups = info.getTrackGroups(rendererIndex); - int trackIndex = C.INDEX_UNSET; + int groupIndex = C.INDEX_UNSET; + int[] tracks = {0} ; if (TextUtils.isEmpty(type)) { type = "default"; @@ -786,7 +826,7 @@ class ReactExoplayerView extends FrameLayout implements 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; + groupIndex = i; break; } } @@ -794,13 +834,56 @@ class ReactExoplayerView extends FrameLayout implements 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; + groupIndex = i; break; } } } else if (type.equals("index")) { if (value.asInt() < groups.length) { - trackIndex = value.asInt(); + groupIndex = value.asInt(); + } + } else if (type.equals("videoHeight")) { + groupIndex = C.INDEX_UNSET; + + /*0 is auto mode. Add all tracks of group 0*/ + if (0 == value.asInt()) { + TrackGroup group = groups.get(0); + + tracks = new int[group.length]; + + groupIndex = 0; + + for (int j = 0; j < group.length; j++) { + tracks[j] = j; + } + } else { + /*Search for the exact video Height*/ + for (int i = 0; i < groups.length; ++i) { + TrackGroup group = groups.get(i); + + for (int j = 0; j < group.length; j++) { + Format format = group.getFormat(j); + + if (format.height == value.asInt()) { + groupIndex = i; + tracks[0] = j; + break; + } + } + } + + /*If exact height not found then make it auto. Add all tracks of group 0*/ + if (groupIndex == C.INDEX_UNSET) { + TrackGroup group = groups.get(0); + + tracks = new int[group.length]; + + groupIndex = 0; + + for (int j = 0; j < group.length; j++) { + tracks[j] = j; + } + } } } else { // default if (rendererIndex == C.TRACK_TYPE_TEXT && Util.SDK_INT > 18 && groups.length > 0) { @@ -808,14 +891,14 @@ class ReactExoplayerView extends FrameLayout implements CaptioningManager captioningManager = (CaptioningManager)themedReactContext.getSystemService(Context.CAPTIONING_SERVICE); if (captioningManager != null && captioningManager.isEnabled()) { - trackIndex = getTrackIndexForDefaultLocale(groups); + groupIndex = getGroupIndexForDefaultLocale(groups); } } else if (rendererIndex == C.TRACK_TYPE_AUDIO) { - trackIndex = getTrackIndexForDefaultLocale(groups); + groupIndex = getGroupIndexForDefaultLocale(groups); } } - if (trackIndex == C.INDEX_UNSET) { + if (groupIndex == C.INDEX_UNSET) { trackSelector.setParameters(disableParameters); return; } @@ -824,24 +907,30 @@ class ReactExoplayerView extends FrameLayout implements .buildUpon() .setRendererDisabled(rendererIndex, false) .setSelectionOverride(rendererIndex, groups, - new DefaultTrackSelector.SelectionOverride(trackIndex, 0)) + new DefaultTrackSelector.SelectionOverride(groupIndex, tracks)) .build(); trackSelector.setParameters(selectionParameters); } - private int getTrackIndexForDefaultLocale(TrackGroupArray groups) { - int trackIndex = 0; // default if no match + private int getGroupIndexForDefaultLocale(TrackGroupArray groups) { + int groupIndex = 0; // default if no match String locale2 = Locale.getDefault().getLanguage(); // 2 letter code String locale3 = Locale.getDefault().getISO3Language(); // 3 letter code for (int i = 0; i < groups.length; ++i) { Format format = groups.get(i).getFormat(0); String language = format.language; if (language != null && (language.equals(locale2) || language.equals(locale3))) { - trackIndex = i; + groupIndex = i; 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) { 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 9ead70ce..f7510335 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java @@ -51,6 +51,9 @@ public class ReactExoplayerViewManager extends ViewGroupManager> createJSModules() { - return Collections.emptyList(); - } - @Override public List createViewManagers(ReactApplicationContext reactContext) { return Collections.singletonList(new ReactExoplayerViewManager());