mirror of
https://github.com/androidx/media.git
synced 2025-05-17 20:49:53 +08:00
Add TrackSelectionUtil getBitratesUsingPastAndFutureInfo
------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=220447459
This commit is contained in:
parent
7a114cb374
commit
5a18a98fbd
@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.google.android.exoplayer2.trackselection;
|
||||
|
||||
import android.support.annotation.VisibleForTesting;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.source.chunk.MediaChunk;
|
||||
@ -77,7 +78,8 @@ public final class TrackSelectionUtil {
|
||||
* estimation can't be calculated, {@link Format#NO_VALUE} is set.
|
||||
* @see #getAverageBitrate(MediaChunkIterator, long)
|
||||
*/
|
||||
public static int[] getBitratesUsingFutureInfo(
|
||||
@VisibleForTesting
|
||||
/* package */ static int[] getBitratesUsingFutureInfo(
|
||||
MediaChunkIterator[] iterators, Format[] formats, long maxDurationUs) {
|
||||
int trackCount = iterators.length;
|
||||
Assertions.checkArgument(trackCount == formats.length);
|
||||
@ -86,6 +88,11 @@ public final class TrackSelectionUtil {
|
||||
}
|
||||
|
||||
int[] bitrates = new int[trackCount];
|
||||
if (maxDurationUs == 0) {
|
||||
Arrays.fill(bitrates, Format.NO_VALUE);
|
||||
return bitrates;
|
||||
}
|
||||
|
||||
int[] formatBitrates = new int[trackCount];
|
||||
float[] bitrateRatios = new float[trackCount];
|
||||
boolean needEstimateBitrate = false;
|
||||
@ -124,10 +131,14 @@ public final class TrackSelectionUtil {
|
||||
* {@link Format#NO_VALUE} is set.
|
||||
* @see #getBitratesUsingFutureInfo(MediaChunkIterator[], Format[], long)
|
||||
*/
|
||||
public static int[] getBitratesUsingPastInfo(
|
||||
@VisibleForTesting
|
||||
/* package */ static int[] getBitratesUsingPastInfo(
|
||||
List<? extends MediaChunk> queue, Format[] formats, long maxDurationUs) {
|
||||
int[] bitrates = new int[formats.length];
|
||||
Arrays.fill(bitrates, Format.NO_VALUE);
|
||||
if (maxDurationUs == 0) {
|
||||
return bitrates;
|
||||
}
|
||||
int queueAverageBitrate = getAverageQueueBitrate(queue, maxDurationUs);
|
||||
if (queueAverageBitrate == Format.NO_VALUE) {
|
||||
return bitrates;
|
||||
@ -141,6 +152,75 @@ public final class TrackSelectionUtil {
|
||||
return bitrates;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns bitrate values for a set of tracks whose formats are given, using the given upcoming
|
||||
* media chunk iterators and the queue of already buffered {@link MediaChunk}s.
|
||||
*
|
||||
* @param iterators An array of {@link MediaChunkIterator}s providing information about the
|
||||
* sequence of upcoming media chunks for each track.
|
||||
* @param queue The queue of already buffered {@link MediaChunk}s. Must not be modified.
|
||||
* @param formats The track formats.
|
||||
* @param maxFutureDurationUs Maximum duration of future chunks to be included in average bitrate
|
||||
* values, in microseconds.
|
||||
* @param maxPastDurationUs Maximum duration of past chunks to be included in average bitrate
|
||||
* values, in microseconds.
|
||||
* @param useFormatBitrateAsLowerBound Whether to return the estimated bitrate only if it's higher
|
||||
* than the bitrate of the track's format.
|
||||
* @return Bitrate values for the tracks. If for a track, a bitrate value can't be calculated,
|
||||
* {@link Format#NO_VALUE} is set.
|
||||
*/
|
||||
public static int[] getBitratesUsingPastAndFutureInfo(
|
||||
MediaChunkIterator[] iterators,
|
||||
List<? extends MediaChunk> queue,
|
||||
Format[] formats,
|
||||
long maxFutureDurationUs,
|
||||
long maxPastDurationUs,
|
||||
boolean useFormatBitrateAsLowerBound) {
|
||||
int[] bitrates = getBitratesUsingFutureInfo(iterators, formats, maxFutureDurationUs);
|
||||
int[] bitratesUsingPastInfo = getBitratesUsingPastInfo(queue, formats, maxPastDurationUs);
|
||||
for (int i = 0; i < bitrates.length; i++) {
|
||||
int bitrate = bitrates[i];
|
||||
if (bitrate == Format.NO_VALUE) {
|
||||
bitrate = bitratesUsingPastInfo[i];
|
||||
}
|
||||
if (bitrate == Format.NO_VALUE
|
||||
|| (useFormatBitrateAsLowerBound
|
||||
&& formats[i].bitrate != Format.NO_VALUE
|
||||
&& bitrate < formats[i].bitrate)) {
|
||||
bitrate = formats[i].bitrate;
|
||||
}
|
||||
bitrates[i] = bitrate;
|
||||
}
|
||||
return bitrates;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills missing values in the given {@code bitrates} array by calculates an estimation using the
|
||||
* closest reference bitrate value.
|
||||
*
|
||||
* @param bitrates An array of bitrates to be filled with estimations. Missing values are set to
|
||||
* {@link Format#NO_VALUE}.
|
||||
* @param formats An array of formats, one for each bitrate.
|
||||
* @param referenceBitrates An array of reference bitrates which are used to calculate
|
||||
* estimations.
|
||||
* @param referenceBitrateRatios An array containing ratio of reference bitrates to their bitrate
|
||||
* estimates.
|
||||
*/
|
||||
private static void estimateBitrates(
|
||||
int[] bitrates, Format[] formats, int[] referenceBitrates, float[] referenceBitrateRatios) {
|
||||
for (int i = 0; i < bitrates.length; i++) {
|
||||
if (bitrates[i] == Format.NO_VALUE) {
|
||||
int formatBitrate = formats[i].bitrate;
|
||||
if (formatBitrate != Format.NO_VALUE) {
|
||||
int closestReferenceBitrateIndex =
|
||||
getClosestBitrateIndex(formatBitrate, referenceBitrates);
|
||||
bitrates[i] =
|
||||
(int) (referenceBitrateRatios[closestReferenceBitrateIndex] * formatBitrate);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int getAverageQueueBitrate(List<? extends MediaChunk> queue, long maxDurationUs) {
|
||||
if (queue.isEmpty()) {
|
||||
return Format.NO_VALUE;
|
||||
@ -162,19 +242,6 @@ public final class TrackSelectionUtil {
|
||||
return queue;
|
||||
}
|
||||
|
||||
private static void estimateBitrates(
|
||||
int[] bitrates, Format[] formats, int[] formatBitrates, float[] bitrateRatios) {
|
||||
for (int i = 0; i < bitrates.length; i++) {
|
||||
if (bitrates[i] == Format.NO_VALUE) {
|
||||
int formatBitrate = formats[i].bitrate;
|
||||
if (formatBitrate != Format.NO_VALUE) {
|
||||
int closestFormat = getClosestBitrateIndex(formatBitrate, formatBitrates);
|
||||
bitrates[i] = (int) (bitrateRatios[closestFormat] * formatBitrate);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int getClosestBitrateIndex(int formatBitrate, int[] formatBitrates) {
|
||||
int closestDistance = Integer.MAX_VALUE;
|
||||
int closestFormat = C.INDEX_UNSET;
|
||||
|
@ -408,6 +408,114 @@ public class TrackSelectionUtilTest {
|
||||
assertThat(bitrates).asList().containsExactly(16).inOrder();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getBitratesUsingPastAndFutureInfo_noPastInfo_returnsBitratesUsingOnlyFutureInfo() {
|
||||
FakeIterator iterator1 =
|
||||
new FakeIterator(
|
||||
/* chunkTimeBoundariesSec= */ new long[] {0, 10}, /* chunkLengths= */ new long[] {10});
|
||||
FakeIterator iterator2 =
|
||||
new FakeIterator(
|
||||
/* chunkTimeBoundariesSec= */ new long[] {0, 5, 15, 30},
|
||||
/* chunkLengths= */ new long[] {10, 20, 30});
|
||||
|
||||
int[] bitrates =
|
||||
TrackSelectionUtil.getBitratesUsingPastAndFutureInfo(
|
||||
new MediaChunkIterator[] {iterator1, iterator2},
|
||||
Collections.emptyList(),
|
||||
new Format[] {createFormatWithBitrate(10), createFormatWithBitrate(20)},
|
||||
MAX_DURATION_US,
|
||||
MAX_DURATION_US,
|
||||
/* useFormatBitrateAsLowerBound= */ false);
|
||||
|
||||
assertThat(bitrates).asList().containsExactly(8, 16).inOrder();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getBitratesUsingPastAndFutureInfo_noFutureInfo_returnsBitratesUsingOnlyPastInfo() {
|
||||
FakeMediaChunk chunk =
|
||||
createChunk(
|
||||
createFormatWithBitrate(10),
|
||||
/* length= */ 10,
|
||||
/* startTimeSec= */ 0,
|
||||
/* endTimeSec= */ 10);
|
||||
|
||||
int[] bitrates =
|
||||
TrackSelectionUtil.getBitratesUsingPastAndFutureInfo(
|
||||
new MediaChunkIterator[] {MediaChunkIterator.EMPTY, MediaChunkIterator.EMPTY},
|
||||
Collections.singletonList(chunk),
|
||||
new Format[] {createFormatWithBitrate(20), createFormatWithBitrate(30)},
|
||||
MAX_DURATION_US,
|
||||
MAX_DURATION_US,
|
||||
/* useFormatBitrateAsLowerBound= */ false);
|
||||
|
||||
assertThat(bitrates).asList().containsExactly(16, 24).inOrder();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void
|
||||
getBitratesUsingPastAndFutureInfo_pastAndFutureInfo_returnsBitratesUsingOnlyFutureInfo() {
|
||||
FakeMediaChunk chunk =
|
||||
createChunk(
|
||||
createFormatWithBitrate(5),
|
||||
/* length= */ 10,
|
||||
/* startTimeSec= */ 0,
|
||||
/* endTimeSec= */ 10);
|
||||
FakeIterator iterator1 =
|
||||
new FakeIterator(
|
||||
/* chunkTimeBoundariesSec= */ new long[] {0, 10}, /* chunkLengths= */ new long[] {10});
|
||||
FakeIterator iterator2 =
|
||||
new FakeIterator(
|
||||
/* chunkTimeBoundariesSec= */ new long[] {0, 5, 15, 30},
|
||||
/* chunkLengths= */ new long[] {10, 20, 30});
|
||||
|
||||
int[] bitrates =
|
||||
TrackSelectionUtil.getBitratesUsingPastAndFutureInfo(
|
||||
new MediaChunkIterator[] {iterator1, iterator2},
|
||||
Collections.singletonList(chunk),
|
||||
new Format[] {createFormatWithBitrate(10), createFormatWithBitrate(20)},
|
||||
MAX_DURATION_US,
|
||||
MAX_DURATION_US,
|
||||
/* useFormatBitrateAsLowerBound= */ false);
|
||||
|
||||
assertThat(bitrates).asList().containsExactly(8, 16).inOrder();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getBitratesUsingPastAndFutureInfo_noPastAndFutureInfo_returnsBitratesOfFormats() {
|
||||
int[] bitrates =
|
||||
TrackSelectionUtil.getBitratesUsingPastAndFutureInfo(
|
||||
new MediaChunkIterator[] {MediaChunkIterator.EMPTY, MediaChunkIterator.EMPTY},
|
||||
Collections.emptyList(),
|
||||
new Format[] {createFormatWithBitrate(10), createFormatWithBitrate(20)},
|
||||
MAX_DURATION_US,
|
||||
MAX_DURATION_US,
|
||||
/* useFormatBitrateAsLowerBound= */ false);
|
||||
|
||||
assertThat(bitrates).asList().containsExactly(10, 20).inOrder();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void
|
||||
getBitratesUsingPastAndFutureInfo_estimatesLowerAndUseFormatBitrateAsLowerBoundTrue_returnsBitratesOfFormats() {
|
||||
FakeMediaChunk chunk =
|
||||
createChunk(
|
||||
createFormatWithBitrate(10),
|
||||
/* length= */ 10,
|
||||
/* startTimeSec= */ 0,
|
||||
/* endTimeSec= */ 10);
|
||||
|
||||
int[] bitrates =
|
||||
TrackSelectionUtil.getBitratesUsingPastAndFutureInfo(
|
||||
new MediaChunkIterator[] {MediaChunkIterator.EMPTY, MediaChunkIterator.EMPTY},
|
||||
Collections.singletonList(chunk),
|
||||
new Format[] {createFormatWithBitrate(20), createFormatWithBitrate(30)},
|
||||
MAX_DURATION_US,
|
||||
MAX_DURATION_US,
|
||||
/* useFormatBitrateAsLowerBound= */ true);
|
||||
|
||||
assertThat(bitrates).asList().containsExactly(20, 30).inOrder();
|
||||
}
|
||||
|
||||
private static FakeMediaChunk createChunk(
|
||||
Format format, int length, int startTimeSec, int endTimeSec) {
|
||||
DataSpec dataSpec = new DataSpec(Uri.EMPTY, 0, length, null, 0);
|
||||
|
Loading…
x
Reference in New Issue
Block a user