mirror of
https://github.com/androidx/media.git
synced 2025-05-17 12:39:52 +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;
|
package com.google.android.exoplayer2.trackselection;
|
||||||
|
|
||||||
|
import android.support.annotation.VisibleForTesting;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
import com.google.android.exoplayer2.Format;
|
import com.google.android.exoplayer2.Format;
|
||||||
import com.google.android.exoplayer2.source.chunk.MediaChunk;
|
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.
|
* estimation can't be calculated, {@link Format#NO_VALUE} is set.
|
||||||
* @see #getAverageBitrate(MediaChunkIterator, long)
|
* @see #getAverageBitrate(MediaChunkIterator, long)
|
||||||
*/
|
*/
|
||||||
public static int[] getBitratesUsingFutureInfo(
|
@VisibleForTesting
|
||||||
|
/* package */ static int[] getBitratesUsingFutureInfo(
|
||||||
MediaChunkIterator[] iterators, Format[] formats, long maxDurationUs) {
|
MediaChunkIterator[] iterators, Format[] formats, long maxDurationUs) {
|
||||||
int trackCount = iterators.length;
|
int trackCount = iterators.length;
|
||||||
Assertions.checkArgument(trackCount == formats.length);
|
Assertions.checkArgument(trackCount == formats.length);
|
||||||
@ -86,6 +88,11 @@ public final class TrackSelectionUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int[] bitrates = new int[trackCount];
|
int[] bitrates = new int[trackCount];
|
||||||
|
if (maxDurationUs == 0) {
|
||||||
|
Arrays.fill(bitrates, Format.NO_VALUE);
|
||||||
|
return bitrates;
|
||||||
|
}
|
||||||
|
|
||||||
int[] formatBitrates = new int[trackCount];
|
int[] formatBitrates = new int[trackCount];
|
||||||
float[] bitrateRatios = new float[trackCount];
|
float[] bitrateRatios = new float[trackCount];
|
||||||
boolean needEstimateBitrate = false;
|
boolean needEstimateBitrate = false;
|
||||||
@ -124,10 +131,14 @@ public final class TrackSelectionUtil {
|
|||||||
* {@link Format#NO_VALUE} is set.
|
* {@link Format#NO_VALUE} is set.
|
||||||
* @see #getBitratesUsingFutureInfo(MediaChunkIterator[], Format[], long)
|
* @see #getBitratesUsingFutureInfo(MediaChunkIterator[], Format[], long)
|
||||||
*/
|
*/
|
||||||
public static int[] getBitratesUsingPastInfo(
|
@VisibleForTesting
|
||||||
|
/* package */ static int[] getBitratesUsingPastInfo(
|
||||||
List<? extends MediaChunk> queue, Format[] formats, long maxDurationUs) {
|
List<? extends MediaChunk> queue, Format[] formats, long maxDurationUs) {
|
||||||
int[] bitrates = new int[formats.length];
|
int[] bitrates = new int[formats.length];
|
||||||
Arrays.fill(bitrates, Format.NO_VALUE);
|
Arrays.fill(bitrates, Format.NO_VALUE);
|
||||||
|
if (maxDurationUs == 0) {
|
||||||
|
return bitrates;
|
||||||
|
}
|
||||||
int queueAverageBitrate = getAverageQueueBitrate(queue, maxDurationUs);
|
int queueAverageBitrate = getAverageQueueBitrate(queue, maxDurationUs);
|
||||||
if (queueAverageBitrate == Format.NO_VALUE) {
|
if (queueAverageBitrate == Format.NO_VALUE) {
|
||||||
return bitrates;
|
return bitrates;
|
||||||
@ -141,6 +152,75 @@ public final class TrackSelectionUtil {
|
|||||||
return bitrates;
|
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) {
|
private static int getAverageQueueBitrate(List<? extends MediaChunk> queue, long maxDurationUs) {
|
||||||
if (queue.isEmpty()) {
|
if (queue.isEmpty()) {
|
||||||
return Format.NO_VALUE;
|
return Format.NO_VALUE;
|
||||||
@ -162,19 +242,6 @@ public final class TrackSelectionUtil {
|
|||||||
return queue;
|
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) {
|
private static int getClosestBitrateIndex(int formatBitrate, int[] formatBitrates) {
|
||||||
int closestDistance = Integer.MAX_VALUE;
|
int closestDistance = Integer.MAX_VALUE;
|
||||||
int closestFormat = C.INDEX_UNSET;
|
int closestFormat = C.INDEX_UNSET;
|
||||||
|
@ -408,6 +408,114 @@ public class TrackSelectionUtilTest {
|
|||||||
assertThat(bitrates).asList().containsExactly(16).inOrder();
|
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(
|
private static FakeMediaChunk createChunk(
|
||||||
Format format, int length, int startTimeSec, int endTimeSec) {
|
Format format, int length, int startTimeSec, int endTimeSec) {
|
||||||
DataSpec dataSpec = new DataSpec(Uri.EMPTY, 0, length, null, 0);
|
DataSpec dataSpec = new DataSpec(Uri.EMPTY, 0, length, null, 0);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user