Skip to content

Commit

Permalink
Merge pull request TheWidlarzGroup#1199 from sridhard/master
Browse files Browse the repository at this point in the history
Youtube like video track selection
  • Loading branch information
cobarx authored Jan 1, 2019
2 parents 1b58d1a + 72f896d commit 04f3f40
Show file tree
Hide file tree
Showing 9 changed files with 187 additions and 30 deletions.
17 changes: 17 additions & 0 deletions Video.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,12 @@ export default class Video extends Component {
}
};

_onBandwidthUpdate = (event) => {
if (this.props.onBandwidthUpdate) {
this.props.onBandwidthUpdate(event.nativeEvent);
}
};

_onSeek = (event) => {
if (this.state.showPoster && !this.props.audioOnly) {
this.setState({showPoster: false});
Expand Down Expand Up @@ -247,6 +253,7 @@ export default class Video extends Component {
onVideoSeek: this._onSeek,
onVideoEnd: this._onEnd,
onVideoBuffer: this._onBuffer,
onVideoBandwidthUpdate: this._onBandwidthUpdate,
onTimedMetadata: this._onTimedMetadata,
onVideoAudioBecomingNoisy: this._onAudioBecomingNoisy,
onVideoExternalPlaybackChange: this._onExternalPlaybackChange,
Expand Down Expand Up @@ -313,6 +320,7 @@ Video.propTypes = {
onVideoBuffer: PropTypes.func,
onVideoError: PropTypes.func,
onVideoProgress: PropTypes.func,
onVideoBandwidthUpdate: PropTypes.func,
onVideoSeek: PropTypes.func,
onVideoEnd: PropTypes.func,
onTimedMetadata: PropTypes.func,
Expand Down Expand Up @@ -344,6 +352,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([
Expand Down Expand Up @@ -377,6 +392,7 @@ Video.propTypes = {
playInBackground: PropTypes.bool,
playWhenInactive: PropTypes.bool,
ignoreSilentSwitch: PropTypes.oneOf(['ignore', 'obey']),
reportBandwidth: PropTypes.bool,
disableFocus: PropTypes.bool,
controls: PropTypes.bool,
audioOnly: PropTypes.bool,
Expand All @@ -391,6 +407,7 @@ Video.propTypes = {
onBuffer: PropTypes.func,
onError: PropTypes.func,
onProgress: PropTypes.func,
onBandwidthUpdate: PropTypes.func,
onSeek: PropTypes.func,
onEnd: PropTypes.func,
onFullscreenPlayerWillPresent: PropTypes.func,
Expand Down
6 changes: 6 additions & 0 deletions android-exoplayer/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ dependencies {
implementation('com.google.android.exoplayer:exoplayer:2.9.3') {
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
implementation "com.android.support:support-annotations:${safeExtGet('supportLibVersion', '+')}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
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;
Expand All @@ -59,6 +60,7 @@
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.upstream.DataSource;
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.util.MimeTypes;
import com.google.android.exoplayer2.util.Util;
Expand All @@ -76,6 +78,7 @@
class ReactExoplayerView extends FrameLayout implements
LifecycleEventListener,
ExoPlayer.EventListener,
BandwidthMeter.EventListener,
BecomingNoisyListener,
AudioManager.OnAudioFocusChangeListener,
MetadataRenderer.Output {
Expand All @@ -85,14 +88,15 @@ class ReactExoplayerView extends FrameLayout implements
private static final DefaultBandwidthMeter BANDWIDTH_METER = new DefaultBandwidthMeter();
private static final CookieManager DEFAULT_COOKIE_MANAGER;
private static final int SHOW_PROGRESS = 1;
private static final int REPORT_BANDWIDTH = 1;

static {
DEFAULT_COOKIE_MANAGER = new CookieManager();
DEFAULT_COOKIE_MANAGER.setCookiePolicy(CookiePolicy.ACCEPT_ORIGINAL_SERVER);
}

private final VideoEventEmitter eventEmitter;

private Handler mainHandler;
private ExoPlayerView exoPlayerView;

Expand Down Expand Up @@ -124,6 +128,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;
Expand All @@ -132,6 +138,7 @@ class ReactExoplayerView extends FrameLayout implements
private float mProgressUpdateInterval = 250.0f;
private boolean playInBackground = false;
private Map<String, String> requestHeaders;
private boolean mReportBandwidth = false;
// \ End props

// React
Expand Down Expand Up @@ -162,8 +169,11 @@ public void handleMessage(Message msg) {
public ReactExoplayerView(ThemedReactContext context) {
super(context);
this.themedReactContext = context;
createViews();

this.eventEmitter = new VideoEventEmitter(context);

createViews();

audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
themedReactContext.addLifecycleEventListener(this);
audioBecomingNoisyReceiver = new AudioBecomingNoisyReceiver(themedReactContext);
Expand Down Expand Up @@ -238,9 +248,15 @@ public void cleanUpResources() {
stopPlayback();
}

//BandwidthMeter.EventListener implementation
@Override
public void onBandwidthSample(int elapsedMs, long bytes, long bitrate) {
if (mReportBandwidth) {
eventEmitter.bandwidthReport(bitrate);
}
}

// Internal methods

private void initializePlayer() {
if (player == null) {
TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory(BANDWIDTH_METER);
Expand All @@ -255,6 +271,7 @@ private void initializePlayer() {
player.setMetadataOutput(this);
exoPlayerView.setPlayer(player);
audioBecomingNoisyReceiver.setListener(this);
BANDWIDTH_METER.addEventListener(new Handler(), this);
setPlayWhenReady(!isPaused);
playerNeedsSource = true;

Expand Down Expand Up @@ -345,6 +362,7 @@ private void releasePlayer() {
progressHandler.removeMessages(SHOW_PROGRESS);
themedReactContext.removeLifecycleEventListener(this);
audioBecomingNoisyReceiver.removeListener();
BANDWIDTH_METER.removeEventListener(this);
}

private boolean requestAudioFocus() {
Expand Down Expand Up @@ -520,12 +538,13 @@ private void videoLoaded() {
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());
}
}

Expand All @@ -546,10 +565,39 @@ private WritableArray getAudioTrackInfo() {
audioTrack.putString("title", format.id != null ? format.id : "");
audioTrack.putString("type", format.sampleMimeType);
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);
}
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() {
WritableArray textTracks = Arguments.createArray();
Expand Down Expand Up @@ -726,6 +774,10 @@ public void setProgressUpdateInterval(final float progressUpdateInterval) {
mProgressUpdateInterval = progressUpdateInterval;
}

public void setReportBandwidth(boolean reportBandwidth) {
mReportBandwidth = reportBandwidth;
}

public void setRawSrc(final Uri uri, final String extension) {
if (uri != null) {
boolean isOriginalSourceNull = srcUri == null;
Expand Down Expand Up @@ -777,7 +829,8 @@ public void setSelectedTrack(int trackType, String type, Dynamic value) {
}

TrackGroupArray groups = info.getTrackGroups(rendererIndex);
int trackIndex = C.INDEX_UNSET;
int groupIndex = C.INDEX_UNSET;
int[] tracks = {0} ;

if (TextUtils.isEmpty(type)) {
type = "default";
Expand All @@ -795,36 +848,54 @@ public void setSelectedTrack(int trackType, String type, Dynamic value) {
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;
}
}
} 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;
groupIndex = i;
break;
}
}
} else if (type.equals("index")) {
if (value.asInt() < groups.length) {
trackIndex = value.asInt();
groupIndex = value.asInt();
}
} else { // default
if (rendererIndex == C.TRACK_TYPE_TEXT && Util.SDK_INT > 18 && groups.length > 0) {
// Use system settings if possible
CaptioningManager captioningManager
= (CaptioningManager)themedReactContext.getSystemService(Context.CAPTIONING_SERVICE);
if (captioningManager != null && captioningManager.isEnabled()) {
trackIndex = getTrackIndexForDefaultLocale(groups);
} else if (type.equals("resolution")) {
int height = value.asInt();
for (int i = 0; i < groups.length; ++i) { // Search for the exact height
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;
}
}
} 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);
return;
}
Expand All @@ -833,28 +904,34 @@ public void setSelectedTrack(int trackType, String type, Dynamic value) {
.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) {
if (groups.length == 0) { // Avoid a crash if we try to select a non-existant group
private int getGroupIndexForDefaultLocale(TrackGroupArray groups) {
if (groups.length == 0){
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 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) {
Expand Down
Loading

0 comments on commit 04f3f40

Please sign in to comment.