Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

VEX-6011: Align AVOD resolutions with available resolutions on Content #14

Merged
merged 3 commits into from
Nov 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Video.js
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,7 @@ Video.propTypes = {
playWhenInactive: PropTypes.bool,
ignoreSilentSwitch: PropTypes.oneOf(['ignore', 'obey']),
reportBandwidth: PropTypes.bool,
contentStartTime: PropTypes.number,
disableFocus: PropTypes.bool,
disableBuffering: PropTypes.bool,
controls: PropTypes.bool,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,13 @@
import com.google.android.exoplayer2.upstream.HttpDataSource;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Util;
import com.google.android.exoplayer2.trackselection.TrackSelection;
import com.google.android.exoplayer2.source.dash.DashUtil;
import com.google.android.exoplayer2.source.dash.manifest.DashManifest;
import com.google.android.exoplayer2.source.dash.manifest.Period;
import com.google.android.exoplayer2.source.dash.manifest.AdaptationSet;
import com.google.android.exoplayer2.source.dash.manifest.Representation;
import com.google.android.exoplayer2.source.dash.manifest.Descriptor;

import java.net.CookieHandler;
import java.net.CookieManager;
Expand All @@ -86,6 +93,13 @@
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.List;
import java.lang.Thread;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.lang.Integer;

@SuppressLint("ViewConstructor")
class ReactExoplayerView extends FrameLayout implements
Expand Down Expand Up @@ -136,6 +150,8 @@ class ReactExoplayerView extends FrameLayout implements
private int maxBitRate = 0;
private long seekTime = C.TIME_UNSET;
private boolean hasDrmFailed = false;
private boolean isUsingContentResolution = false;
private boolean selectTrackWhenReady = false;

private int minBufferMs = DefaultLoadControl.DEFAULT_MIN_BUFFER_MS;
private int maxBufferMs = DefaultLoadControl.DEFAULT_MAX_BUFFER_MS;
Expand All @@ -159,6 +175,7 @@ class ReactExoplayerView extends FrameLayout implements
private ReadableArray textTracks;
private boolean disableFocus;
private boolean disableBuffering;
private long contentStartTime;
private boolean disableDisconnectError;
private boolean preventsDisplaySleepDuringVideoPlayback = true;
private float mProgressUpdateInterval = 250.0f;
Expand Down Expand Up @@ -850,6 +867,10 @@ public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
onBuffering(false);
startProgressHandler();
videoLoaded();
if (selectTrackWhenReady && isUsingContentResolution) {
selectTrackWhenReady = false;
setSelectedTrack(C.TRACK_TYPE_VIDEO, videoTrackType, videoTrackValue);
}
// Setting the visibility for the playerControlView
if (playerControlView != null) {
playerControlView.show();
Expand Down Expand Up @@ -921,6 +942,13 @@ private WritableArray getAudioTrackInfo() {
return audioTracks;
}
private WritableArray getVideoTrackInfo() {

WritableArray contentVideoTracks = this.getVideoTrackInfoFromManifest();
if (contentVideoTracks != null) {
isUsingContentResolution = true;
return contentVideoTracks;
}

WritableArray videoTracks = Arguments.createArray();

MappingTrackSelector.MappedTrackInfo info = trackSelector.getCurrentMappedTrackInfo();
Expand All @@ -947,9 +975,73 @@ private WritableArray getVideoTrackInfo() {
}
}
}

return videoTracks;
}

private WritableArray getVideoTrackInfoFromManifest() {
ExecutorService es = Executors.newSingleThreadExecutor();
final DataSource dataSource = this.mediaDataSourceFactory.createDataSource();
final Uri sourceUri = this.srcUri;
final Timeline timelineRef = this.player.getCurrentTimeline();
final long startTime = this.contentStartTime * 1000 - 100; // s -> ms with 100ms offset

Future<WritableArray> result = es.submit(new Callable<WritableArray>() {
DataSource ds = dataSource;
Uri uri = sourceUri;
Timeline timeline = timelineRef;
long startTimeUs = startTime * 1000; // ms -> us

public WritableArray call() throws Exception {
WritableArray videoTracks = Arguments.createArray();
try {
DashManifest manifest = DashUtil.loadManifest(this.ds, this.uri);
int periodCount = manifest.getPeriodCount();
for (int i = 0; i < periodCount; i++) {
Period period = manifest.getPeriod(i);
for (int adaptationIndex = 0; adaptationIndex < period.adaptationSets.size(); adaptationIndex++) {
AdaptationSet adaptation = period.adaptationSets.get(adaptationIndex);
if (adaptation.type != C.TRACK_TYPE_VIDEO) {
continue;
}
boolean hasFoundContentPeriod = false;
for (int representationIndex = 0; representationIndex < adaptation.representations.size(); representationIndex++) {
Representation representation = adaptation.representations.get(representationIndex);
Format format = representation.format;
if (representation.presentationTimeOffsetUs <= startTimeUs) {
break;
}
hasFoundContentPeriod = true;
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(representationIndex) : format.id);
if (isFormatSupported(format)) {
videoTracks.pushMap(videoTrack);
}
}
if (hasFoundContentPeriod) {
return videoTracks;
}
}
}
} catch (Exception e) {}
return null;
}
});

try {
WritableArray results = result.get();
es.shutdown();
return results;
} catch (Exception e) {}

return null;
}

private WritableArray getTextTrackInfo() {
WritableArray textTracks = Arguments.createArray();

Expand Down Expand Up @@ -993,6 +1085,11 @@ public void onPositionDiscontinuity(int reason) {
// which they seeked.
updateResumePosition();
}
if (isUsingContentResolution) {
// Discontinuity events might have a different track list so we update the selected track
setSelectedTrack(C.TRACK_TYPE_VIDEO, videoTrackType, videoTrackValue);
selectTrackWhenReady = true;
}
// When repeat is turned on, reaching the end of the video will not cause a state change
// so we need to explicitly detect it.
if (reason == Player.DISCONTINUITY_REASON_PERIOD_TRANSITION
Expand All @@ -1010,6 +1107,10 @@ public void onTimelineChanged(Timeline timeline, Object manifest, int reason) {
public void onSeekProcessed() {
eventEmitter.seek(player.getCurrentPosition(), seekTime);
seekTime = C.TIME_UNSET;
if (isUsingContentResolution) {
// We need to update the selected track to make sure that it still matches user selection if track list has changed in this period
setSelectedTrack(C.TRACK_TYPE_VIDEO, videoTrackType, videoTrackValue);
}
}

@Override
Expand Down Expand Up @@ -1278,14 +1379,51 @@ public void setSelectedTrack(int trackType, String type, Dynamic value) {
int height = value.asInt();
for (int i = 0; i < groups.length; ++i) { // Search for the exact height
TrackGroup group = groups.get(i);
Format closestFormat = null;
int closestTrackIndex = -1;
boolean usingExactMatch = false;
for (int j = 0; j < group.length; j++) {
Format format = group.getFormat(j);
if (format.height == height) {
groupIndex = i;
tracks[0] = j;
closestFormat = null;
closestTrackIndex = -1;
usingExactMatch = true;
break;
} else if (isUsingContentResolution) {
// When using content resolution rather than ads, we need to try and find the closest match if there is no exact match
if (closestFormat != null) {
if ((format.bitrate > closestFormat.bitrate || format.height > closestFormat.height) && format.height < height) {
// Higher quality match
closestFormat = format;
closestTrackIndex = j;
}
} else if(format.height < height) {
closestFormat = format;
closestTrackIndex = j;
}
}
}
// This is a fallback if the new period contains only higher resolutions than the user has selected
if (closestFormat == null && isUsingContentResolution && !usingExactMatch) {
// No close match found - so we pick the lowest quality
int minHeight = Integer.MAX_VALUE;
for (int j = 0; j < group.length; j++) {
Format format = group.getFormat(j);
if (format.height < minHeight) {
minHeight = format.height;
groupIndex = i;
tracks[0] = j;
}
}
}
// Selecting the closest match found
if (closestFormat != null && closestTrackIndex != -1) {
// We found the closest match instead of an exact one
groupIndex = i;
tracks[0] = closestTrackIndex;
}
}
} else if (rendererIndex == C.TRACK_TYPE_TEXT && Util.SDK_INT > 18) { // Text default
// Use system settings if possible
Expand Down Expand Up @@ -1468,6 +1606,10 @@ public void setBackBufferDurationMs(int backBufferDurationMs) {
this.backBufferDurationMs = backBufferDurationMs;
}

public void setContentStartTime(int contentStartTime) {
this.contentStartTime = (long)contentStartTime;
}

public void setDisableBuffering(boolean disableBuffering) {
this.disableBuffering = disableBuffering;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ public class ReactExoplayerViewManager extends ViewGroupManager<ReactExoplayerVi
private static final String PROP_MIN_LOAD_RETRY_COUNT = "minLoadRetryCount";
private static final String PROP_MAXIMUM_BIT_RATE = "maxBitRate";
private static final String PROP_PLAY_IN_BACKGROUND = "playInBackground";
private static final String PROP_CONTENT_START_TIME = "contentStartTime";
private static final String PROP_DISABLE_FOCUS = "disableFocus";
private static final String PROP_DISABLE_BUFFERING = "disableBuffering";
private static final String PROP_DISABLE_DISCONNECT_ERROR = "disableDisconnectError";
Expand Down Expand Up @@ -302,6 +303,11 @@ public void setBackBufferDurationMs(final ReactExoplayerView videoView, final in
videoView.setBackBufferDurationMs(backBufferDurationMs);
}

@ReactProp(name = PROP_CONTENT_START_TIME, defaultInt = 0)
public void setContentStartTime(final ReactExoplayerView videoView, final int contentStartTime) {
videoView.setContentStartTime(contentStartTime);
}

@ReactProp(name = PROP_DISABLE_BUFFERING, defaultBoolean = false)
public void setDisableBuffering(final ReactExoplayerView videoView, final boolean disableBuffering) {
videoView.setDisableBuffering(disableBuffering);
Expand Down