Skip to content

Commit 5d93baf

Browse files
committed
exoplayer: upd sabr classes
1 parent 8f6803e commit 5d93baf

File tree

3 files changed

+109
-44
lines changed

3 files changed

+109
-44
lines changed

exoplayer-amzn-2.10.6/library/sabr/src/main/java/com/google/android/exoplayer2/source/sabr/DefaultSabrChunkSource.java

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,16 @@
99
import com.google.android.exoplayer2.C;
1010
import com.google.android.exoplayer2.Format;
1111
import com.google.android.exoplayer2.SeekParameters;
12-
import com.google.android.exoplayer2.extractor.ChunkIndex;
1312
import com.google.android.exoplayer2.extractor.Extractor;
14-
import com.google.android.exoplayer2.extractor.SeekMap;
1513
import com.google.android.exoplayer2.extractor.TrackOutput;
1614
import com.google.android.exoplayer2.source.BehindLiveWindowException;
17-
import com.google.android.exoplayer2.source.chunk.BaseMediaChunkIterator;
1815
import com.google.android.exoplayer2.source.chunk.Chunk;
1916
import com.google.android.exoplayer2.source.chunk.ChunkExtractorWrapper;
2017
import com.google.android.exoplayer2.source.chunk.ChunkHolder;
2118
import com.google.android.exoplayer2.source.chunk.ContainerMediaChunk;
2219
import com.google.android.exoplayer2.source.chunk.InitializationChunk;
2320
import com.google.android.exoplayer2.source.chunk.MediaChunk;
2421
import com.google.android.exoplayer2.source.chunk.MediaChunkIterator;
25-
import com.google.android.exoplayer2.source.chunk.SingleSampleMediaChunk;
2622
import com.google.android.exoplayer2.source.sabr.PlayerEmsgHandler.PlayerTrackEmsgHandler;
2723
import com.google.android.exoplayer2.source.sabr.manifest.AdaptationSet;
2824
import com.google.android.exoplayer2.source.sabr.manifest.RangedUri;
@@ -38,11 +34,9 @@
3834
import com.google.android.exoplayer2.trackselection.TrackSelection;
3935
import com.google.android.exoplayer2.upstream.DataSource;
4036
import com.google.android.exoplayer2.upstream.DataSpec;
41-
import com.google.android.exoplayer2.upstream.HttpDataSource.InvalidResponseCodeException;
4237
import com.google.android.exoplayer2.upstream.LoaderErrorThrower;
4338
import com.google.android.exoplayer2.upstream.TransferListener;
4439
import com.google.android.exoplayer2.util.MimeTypes;
45-
import com.google.android.exoplayer2.util.Util;
4640
import com.liskovsoft.sharedutils.helpers.Helpers;
4741

4842
import java.io.IOException;
@@ -315,7 +309,8 @@ public void getNextChunk(long playbackPositionUs, long loadPositionUs, List<? ex
315309
//if (representationHolder.segmentIndex == null) {
316310
// pendingIndexUri = selectedRepresentation.getIndexUri();
317311
//}
318-
if (pendingInitializationUri != null || pendingIndexUri != null) {
312+
//if (pendingInitializationUri != null || pendingIndexUri != null) {
313+
if (pendingInitializationUri != null) {
319314
// We have initialization and/or index requests to make.
320315
out.chunk = newInitializationChunk(representationHolder, dataSource,
321316
trackSelection.getSelectedFormat(), trackSelection.getSelectionReason(),
@@ -516,7 +511,7 @@ protected Chunk newInitializationChunk(
516511
DataSpec dataSpec = new DataSpec(
517512
requestUri.resolveUri(baseUrl),
518513
DataSpec.HTTP_METHOD_POST,
519-
sabrStream.buildInitVideoPlaybackAbrRequest(trackType).toByteArray(),
514+
sabrStream.buildVideoPlaybackAbrRequest(trackType, true).toByteArray(),
520515
0, 0, C.LENGTH_UNSET,
521516
//requestUri.start,
522517
//requestUri.start,
@@ -552,7 +547,7 @@ protected Chunk newMediaChunk(
552547
Uri.parse(baseUrl),
553548
//segmentUri.resolveUri(baseUrl),
554549
DataSpec.HTTP_METHOD_POST,
555-
sabrStream.buildInitVideoPlaybackAbrRequest(trackType).toByteArray(),
550+
sabrStream.buildVideoPlaybackAbrRequest(trackType, false).toByteArray(),
556551
0, 0, C.LENGTH_UNSET,
557552
//segmentUri.start,
558553
//segmentUri.start,

exoplayer-amzn-2.10.6/library/sabr/src/main/java/com/google/android/exoplayer2/source/sabr/parser/SabrStream.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -169,8 +169,8 @@ public SabrPart parse(@NonNull ExtractorInput extractorInput) {
169169
return result != null ? result : multiResult != null && !multiResult.isEmpty() ? multiResult.remove(0) : null;
170170
}
171171

172-
public VideoPlaybackAbrRequest buildInitVideoPlaybackAbrRequest(int trackType) {
173-
return processor.buildInitVideoPlaybackAbrRequest(trackType);
172+
public VideoPlaybackAbrRequest buildVideoPlaybackAbrRequest(int trackType, boolean isInit) {
173+
return processor.buildVideoPlaybackAbrRequest(trackType, isInit);
174174
}
175175

176176
public void setAudioSelection(AudioSelector audioFormatSelector) {

exoplayer-amzn-2.10.6/library/sabr/src/main/java/com/google/android/exoplayer2/source/sabr/parser/processor/SabrProcessor.java

Lines changed: 103 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.google.android.exoplayer2.source.sabr.parser.processor;
22

33
import android.util.Base64;
4+
import android.util.Pair;
45

56
import androidx.annotation.NonNull;
67

@@ -683,8 +684,8 @@ public int getLiveSegmentTargetDurationSec() {
683684
// .build();
684685
//}
685686

686-
public VideoPlaybackAbrRequest buildInitVideoPlaybackAbrRequest(int trackType) {
687-
int resolution = 1080; // ???
687+
public VideoPlaybackAbrRequest buildVideoPlaybackAbrRequest(int trackType, boolean isInit) {
688+
int resolution = trackType == C.TRACK_TYPE_VIDEO ? 720 : 0; // current track height (if video)
688689
int bandwidthEstimate = 1350000; // ???
689690

690691
ClientAbrState clientAbrStateTest = ClientAbrState.newBuilder()
@@ -695,14 +696,26 @@ public VideoPlaybackAbrRequest buildInitVideoPlaybackAbrRequest(int trackType) {
695696
.setDrcEnabled(false)
696697
.setSabrForceMaxNetworkInterruptionDurationMs(0)
697698
.build();
698-
699+
700+
Pair<List<BufferedRange>, FormatId> bufferRanges = createBufferedRangesAndDiscardFormat(trackType);
701+
702+
List<FormatId> selectedFormats = getSelectedFormatIds(trackType);
703+
704+
if (isInit) {
705+
selectedFormats.clear();
706+
}
707+
708+
if (bufferRanges.second != null) {
709+
selectedFormats.add(0, bufferRanges.second);
710+
}
711+
699712
return VideoPlaybackAbrRequest.newBuilder()
700713
.setClientAbrState(clientAbrStateTest)
701714
.addAllPreferredVideoFormatIds(videoFormatSelector.formatIds)
702715
.addAllPreferredAudioFormatIds(audioFormatSelector.formatIds)
703716
.addAllPreferredSubtitleFormatIds(captionFormatSelector.formatIds)
704-
.addAllSelectedFormatIds(getAllSelectedFormatIds(trackType))
705-
.addAllBufferedRanges(createAllBufferedRanges(trackType))
717+
.addAllSelectedFormatIds(selectedFormats)
718+
.addAllBufferedRanges(bufferRanges.first)
706719
.setVideoPlaybackUstreamerConfig(
707720
ByteString.copyFrom(
708721
Base64.decode(videoPlaybackUstreamerConfig, Base64.URL_SAFE)
@@ -734,15 +747,15 @@ private int getAllSelectedTracksBitfield(int trackType) {
734747
return 0;
735748
}
736749

737-
private List<FormatId> getAllSelectedFormatIds(int trackType) {
750+
private List<FormatId> getSelectedFormatIds(int trackType) {
738751
List<FormatId> result = new ArrayList<>();
739752

740753
if (!videoFormatSelector.formatIds.isEmpty() && trackType == C.TRACK_TYPE_VIDEO) {
741-
return videoFormatSelector.formatIds;
754+
result = videoFormatSelector.formatIds;
742755
} else if (!audioFormatSelector.formatIds.isEmpty() && trackType == C.TRACK_TYPE_AUDIO) {
743-
return audioFormatSelector.formatIds;
756+
result = audioFormatSelector.formatIds;
744757
} else if (!captionFormatSelector.formatIds.isEmpty() && trackType == C.TRACK_TYPE_TEXT) {
745-
return captionFormatSelector.formatIds;
758+
result = captionFormatSelector.formatIds;
746759
}
747760

748761
return result;
@@ -820,30 +833,6 @@ private List<BufferedRange> createBufferedRanges() {
820833
return result;
821834
}
822835

823-
private List<BufferedRange> createAllBufferedRanges(int trackType) {
824-
List<FormatId> allSelectedFormatIds = getAllSelectedFormatIds(trackType);
825-
826-
int durationMs = 2147483647; // ???
827-
828-
TimeRange timeRange = TimeRange.newBuilder()
829-
.setStartTicks(0)
830-
.setDurationTicks(durationMs)
831-
.setTimescale(1_000)
832-
.build();
833-
BufferedRange bufferedRange = BufferedRange.newBuilder()
834-
.setFormatId(allSelectedFormatIds.get(0))
835-
.setStartTimeMs(0)
836-
.setDurationMs(durationMs)
837-
.setStartSegmentIndex((int) durationMs)
838-
.setEndSegmentIndex((int) durationMs)
839-
.setTimeRange(timeRange)
840-
.build();
841-
List<BufferedRange> bufferedRanges = new ArrayList<>();
842-
bufferedRanges.add(bufferedRange);
843-
844-
return bufferedRanges;
845-
}
846-
847836
private FormatSelector matchFormatSelector(FormatInitializationMetadata formatInitMetadata) {
848837
for (FormatSelector formatSelector : new FormatSelector[]{videoFormatSelector, audioFormatSelector, captionFormatSelector}) {
849838
if (formatSelector == null) {
@@ -872,4 +861,85 @@ public void setCaptionFormatSelector(CaptionSelector captionFormatSelector) {
872861
this.captionFormatSelector = captionFormatSelector;
873862
initializeClientAbrState();
874863
}
864+
865+
/**
866+
* Adds buffering information to the ABR request for all active formats.<br/><br/>
867+
*
868+
* NOTE:
869+
* On the web, mobile, and TV clients, buffered ranges in combination to player time is what dictates the segments you get.
870+
* In our case, we are cheating a bit by abusing the player time field (in clientAbrState), setting it to the exact start
871+
* time value of the segment we want, while YouTube simply uses the actual player time.<br/><br/>
872+
*
873+
* We don't have to fully replicate this behavior for two reasons:
874+
* 1. The SABR server will only send so much segments for a given player time. That means players like Shaka would
875+
* not be able to buffer more than what the server thinks is enough. It would behave like YouTube's.
876+
* 2. We don't have to know what segment a buffered range starts/ends at. It is easy to do in Shaka, but not in other players.
877+
*
878+
* @return The format to discard (if any) - typically formats that are active but not currently requested.
879+
*/
880+
private Pair<List<BufferedRange>, FormatId> createBufferedRangesAndDiscardFormat(int trackType) {
881+
FormatId audioFormat = audioFormatSelector.formatIds.isEmpty() ? null : audioFormatSelector.formatIds.get(0);
882+
FormatId videoFormat = videoFormatSelector.formatIds.isEmpty() ? null : videoFormatSelector.formatIds.get(0);
883+
884+
FormatId formatToDiscard = null;
885+
List<BufferedRange> bufferedRanges = new ArrayList<>();
886+
887+
FormatId currentFormat = trackType == C.TRACK_TYPE_VIDEO ? videoFormat : audioFormat;
888+
int currentFormatITag = currentFormat != null ? currentFormat.getItag() : -1;
889+
890+
for (FormatId activeFormat : new FormatId[]{videoFormat, audioFormat}) {
891+
if (activeFormat == null) {
892+
continue;
893+
}
894+
895+
boolean shouldDiscard = currentFormatITag != activeFormat.getItag();
896+
FormatId initializedFormat = null;
897+
898+
BufferedRange bufferedRange = shouldDiscard ? createFullBufferRange(activeFormat) : createPartialBufferRange(initializedFormat);
899+
900+
if (bufferedRange != null) {
901+
bufferedRanges.add(bufferedRange);
902+
903+
if (shouldDiscard) {
904+
formatToDiscard = activeFormat;
905+
}
906+
}
907+
}
908+
909+
return new Pair<>(bufferedRanges, formatToDiscard);
910+
}
911+
912+
/**
913+
* Creates a bogus buffered range for a format. Used when we want to signal to the server to not send any
914+
* segments for this format.
915+
* @param format - The format to create a full buffer range for.
916+
* @return A BufferedRange object indicating the entire format is buffered.
917+
*/
918+
private BufferedRange createFullBufferRange(@NonNull FormatId format) {
919+
return BufferedRange.newBuilder()
920+
.setFormatId(format)
921+
.setDurationMs(Integer.MAX_VALUE)
922+
.setStartTimeMs(0)
923+
.setStartSegmentIndex(Integer.MAX_VALUE)
924+
.setEndSegmentIndex(Integer.MAX_VALUE)
925+
.setTimeRange(TimeRange.newBuilder()
926+
.setDurationTicks(Integer.MAX_VALUE)
927+
.setStartTicks(0)
928+
.setTimescale(1_000)
929+
.build())
930+
.build();
931+
}
932+
933+
/**
934+
* Creates a buffered range representing a partially buffered format.
935+
* @param initializedFormat - The format with initialization data.
936+
* @return A BufferedRange object with segment information, or null if no metadata is available.
937+
*/
938+
private BufferedRange createPartialBufferRange(FormatId initializedFormat) {
939+
if (initializedFormat == null) {
940+
return null;
941+
}
942+
943+
return null;
944+
}
875945
}

0 commit comments

Comments
 (0)