mirror of
https://github.com/androidx/media.git
synced 2025-05-08 16:10:38 +08:00
Clean up max dimension handling.
This commit is contained in:
parent
4a9ff7b094
commit
cb85dc25aa
@ -18,7 +18,6 @@ package com.google.android.exoplayer.dash;
|
|||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import com.google.android.exoplayer.MediaFormat;
|
|
||||||
import com.google.android.exoplayer.TimeRange;
|
import com.google.android.exoplayer.TimeRange;
|
||||||
import com.google.android.exoplayer.TrackRenderer;
|
import com.google.android.exoplayer.TrackRenderer;
|
||||||
import com.google.android.exoplayer.chunk.ChunkOperationHolder;
|
import com.google.android.exoplayer.chunk.ChunkOperationHolder;
|
||||||
@ -85,23 +84,14 @@ public class DashChunkSourceTest extends InstrumentationTestCase {
|
|||||||
private static final Format WIDE_VIDEO =
|
private static final Format WIDE_VIDEO =
|
||||||
new Format("3", "video/mp4", WIDE_WIDTH, 50, -1, -1, -1, 1000);
|
new Format("3", "video/mp4", WIDE_WIDTH, 50, -1, -1, -1, 1000);
|
||||||
|
|
||||||
@Mock private DataSource mockDataSource;
|
@Mock
|
||||||
|
private DataSource mockDataSource;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
TestUtil.setUpMockito(this);
|
TestUtil.setUpMockito(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testMaxVideoDimensions() {
|
|
||||||
DashChunkSource chunkSource = new DashChunkSource(generateVodMpd(), AdaptationSet.TYPE_VIDEO,
|
|
||||||
null, null, null);
|
|
||||||
MediaFormat format = MediaFormat.createVideoFormat("video/h264", 5000, 1, 1, 1, 1, 1, null);
|
|
||||||
format = chunkSource.getWithMaxVideoDimensions(format);
|
|
||||||
|
|
||||||
assertEquals(WIDE_WIDTH, format.maxWidth);
|
|
||||||
assertEquals(TALL_HEIGHT, format.maxHeight);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testGetAvailableRangeOnVod() {
|
public void testGetAvailableRangeOnVod() {
|
||||||
DashChunkSource chunkSource = new DashChunkSource(generateVodMpd(), AdaptationSet.TYPE_VIDEO,
|
DashChunkSource chunkSource = new DashChunkSource(generateVodMpd(), AdaptationSet.TYPE_VIDEO,
|
||||||
null, null, mock(FormatEvaluator.class));
|
null, null, mock(FormatEvaluator.class));
|
||||||
@ -192,22 +182,6 @@ public class DashChunkSourceTest extends InstrumentationTestCase {
|
|||||||
checkSegmentRequestSequenceOnMultiPeriodLive(chunkSource);
|
checkSegmentRequestSequenceOnMultiPeriodLive(chunkSource);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testMaxVideoDimensionsLegacy() {
|
|
||||||
SingleSegmentBase segmentBase1 = new SingleSegmentBase("https://example.com/1.mp4");
|
|
||||||
Representation representation1 =
|
|
||||||
Representation.newInstance(0, 0, null, 0, TALL_VIDEO, segmentBase1);
|
|
||||||
|
|
||||||
SingleSegmentBase segmentBase2 = new SingleSegmentBase("https://example.com/2.mp4");
|
|
||||||
Representation representation2 =
|
|
||||||
Representation.newInstance(0, 0, null, 0, WIDE_VIDEO, segmentBase2);
|
|
||||||
|
|
||||||
DashChunkSource chunkSource = new DashChunkSource(null, null, representation1, representation2);
|
|
||||||
MediaFormat format = MediaFormat.createVideoFormat("video/h264", 5000, 1, 1, 1, 1, 1, null);
|
|
||||||
format = chunkSource.getWithMaxVideoDimensions(format);
|
|
||||||
|
|
||||||
assertEquals(WIDE_WIDTH, format.maxWidth);
|
|
||||||
assertEquals(TALL_HEIGHT, format.maxHeight);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testLiveEdgeNoLatency() {
|
public void testLiveEdgeNoLatency() {
|
||||||
long startTimeMs = 0;
|
long startTimeMs = 0;
|
||||||
|
@ -323,25 +323,12 @@ public final class MediaFormat {
|
|||||||
if (obj == null || getClass() != obj.getClass()) {
|
if (obj == null || getClass() != obj.getClass()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return equalsInternal((MediaFormat) obj, false);
|
MediaFormat other = (MediaFormat) obj;
|
||||||
}
|
|
||||||
|
|
||||||
public boolean equals(MediaFormat other, boolean ignoreMaxDimensions) {
|
|
||||||
if (this == other) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (other == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return equalsInternal(other, ignoreMaxDimensions);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean equalsInternal(MediaFormat other, boolean ignoreMaxDimensions) {
|
|
||||||
if (adaptive != other.adaptive || bitrate != other.bitrate || maxInputSize != other.maxInputSize
|
if (adaptive != other.adaptive || bitrate != other.bitrate || maxInputSize != other.maxInputSize
|
||||||
|| width != other.width || height != other.height
|
|| width != other.width || height != other.height
|
||||||
|| rotationDegrees != other.rotationDegrees
|
|| rotationDegrees != other.rotationDegrees
|
||||||
|| pixelWidthHeightRatio != other.pixelWidthHeightRatio
|
|| pixelWidthHeightRatio != other.pixelWidthHeightRatio
|
||||||
|| (!ignoreMaxDimensions && (maxWidth != other.maxWidth || maxHeight != other.maxHeight))
|
|| maxWidth != other.maxWidth || maxHeight != other.maxHeight
|
||||||
|| channelCount != other.channelCount || sampleRate != other.sampleRate
|
|| channelCount != other.channelCount || sampleRate != other.sampleRate
|
||||||
|| !Util.areEqual(language, other.language) || !Util.areEqual(mimeType, other.mimeType)
|
|| !Util.areEqual(language, other.language) || !Util.areEqual(mimeType, other.mimeType)
|
||||||
|| initializationData.size() != other.initializationData.size()) {
|
|| initializationData.size() != other.initializationData.size()) {
|
||||||
|
@ -232,8 +232,7 @@ public class ChunkSampleSource implements SampleSource, SampleSourceReader, Load
|
|||||||
|
|
||||||
if (haveSamples || currentChunk.isMediaFormatFinal) {
|
if (haveSamples || currentChunk.isMediaFormatFinal) {
|
||||||
MediaFormat mediaFormat = currentChunk.getMediaFormat();
|
MediaFormat mediaFormat = currentChunk.getMediaFormat();
|
||||||
if (!mediaFormat.equals(downstreamMediaFormat, true)) {
|
if (!mediaFormat.equals(downstreamMediaFormat)) {
|
||||||
mediaFormat = chunkSource.getWithMaxVideoDimensions(mediaFormat);
|
|
||||||
formatHolder.format = mediaFormat;
|
formatHolder.format = mediaFormat;
|
||||||
formatHolder.drmInitData = currentChunk.getDrmInitData();
|
formatHolder.drmInitData = currentChunk.getDrmInitData();
|
||||||
downstreamMediaFormat = mediaFormat;
|
downstreamMediaFormat = mediaFormat;
|
||||||
|
@ -76,19 +76,6 @@ public interface ChunkSource {
|
|||||||
*/
|
*/
|
||||||
void enable(int track);
|
void enable(int track);
|
||||||
|
|
||||||
/**
|
|
||||||
* Adaptive video {@link ChunkSource} implementations must return a copy of the provided
|
|
||||||
* {@link MediaFormat} with the maximum video dimensions set. Other implementations can return
|
|
||||||
* the provided {@link MediaFormat} directly.
|
|
||||||
* <p>
|
|
||||||
* This method should only be called when the source is enabled.
|
|
||||||
*
|
|
||||||
* @param format The format to be copied or returned.
|
|
||||||
* @return A copy of the provided {@link MediaFormat} with the maximum video dimensions set, or
|
|
||||||
* the provided format.
|
|
||||||
*/
|
|
||||||
MediaFormat getWithMaxVideoDimensions(MediaFormat format);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates to the source that it should still be checking for updates to the stream.
|
* Indicates to the source that it should still be checking for updates to the stream.
|
||||||
* <p>
|
* <p>
|
||||||
|
@ -36,6 +36,8 @@ public class ContainerMediaChunk extends BaseMediaChunk implements SingleTrackOu
|
|||||||
|
|
||||||
private final ChunkExtractorWrapper extractorWrapper;
|
private final ChunkExtractorWrapper extractorWrapper;
|
||||||
private final long sampleOffsetUs;
|
private final long sampleOffsetUs;
|
||||||
|
private final int adaptiveMaxWidth;
|
||||||
|
private final int adaptiveMaxHeight;
|
||||||
|
|
||||||
private MediaFormat mediaFormat;
|
private MediaFormat mediaFormat;
|
||||||
private DrmInitData drmInitData;
|
private DrmInitData drmInitData;
|
||||||
@ -56,6 +58,12 @@ public class ContainerMediaChunk extends BaseMediaChunk implements SingleTrackOu
|
|||||||
* @param extractorWrapper A wrapped extractor to use for parsing the data.
|
* @param extractorWrapper A wrapped extractor to use for parsing the data.
|
||||||
* @param mediaFormat The {@link MediaFormat} of the chunk, if known. May be null if the data is
|
* @param mediaFormat The {@link MediaFormat} of the chunk, if known. May be null if the data is
|
||||||
* known to define its own format.
|
* known to define its own format.
|
||||||
|
* @param adaptiveMaxWidth If this chunk contains video and is part of an adaptive playback, this
|
||||||
|
* is the maximum width of the video in pixels that will be encountered during the playback.
|
||||||
|
* {@link MediaFormat#NO_VALUE} otherwise.
|
||||||
|
* @param adaptiveMaxHeight If this chunk contains video and is part of an adaptive playback, this
|
||||||
|
* is the maximum height of the video in pixels that will be encountered during the playback.
|
||||||
|
* {@link MediaFormat#NO_VALUE} otherwise.
|
||||||
* @param drmInitData The {@link DrmInitData} for the chunk. Null if the media is not drm
|
* @param drmInitData The {@link DrmInitData} for the chunk. Null if the media is not drm
|
||||||
* protected. May also be null if the data is known to define its own initialization data.
|
* protected. May also be null if the data is known to define its own initialization data.
|
||||||
* @param isMediaFormatFinal True if {@code mediaFormat} and {@code drmInitData} are known to be
|
* @param isMediaFormatFinal True if {@code mediaFormat} and {@code drmInitData} are known to be
|
||||||
@ -64,13 +72,16 @@ public class ContainerMediaChunk extends BaseMediaChunk implements SingleTrackOu
|
|||||||
*/
|
*/
|
||||||
public ContainerMediaChunk(DataSource dataSource, DataSpec dataSpec, int trigger, Format format,
|
public ContainerMediaChunk(DataSource dataSource, DataSpec dataSpec, int trigger, Format format,
|
||||||
long startTimeUs, long endTimeUs, int chunkIndex, boolean isLastChunk, long sampleOffsetUs,
|
long startTimeUs, long endTimeUs, int chunkIndex, boolean isLastChunk, long sampleOffsetUs,
|
||||||
ChunkExtractorWrapper extractorWrapper, MediaFormat mediaFormat, DrmInitData drmInitData,
|
ChunkExtractorWrapper extractorWrapper, MediaFormat mediaFormat, int adaptiveMaxWidth,
|
||||||
boolean isMediaFormatFinal, int parentId) {
|
int adaptiveMaxHeight, DrmInitData drmInitData, boolean isMediaFormatFinal, int parentId) {
|
||||||
super(dataSource, dataSpec, trigger, format, startTimeUs, endTimeUs, chunkIndex, isLastChunk,
|
super(dataSource, dataSpec, trigger, format, startTimeUs, endTimeUs, chunkIndex, isLastChunk,
|
||||||
isMediaFormatFinal, parentId);
|
isMediaFormatFinal, parentId);
|
||||||
this.extractorWrapper = extractorWrapper;
|
this.extractorWrapper = extractorWrapper;
|
||||||
this.sampleOffsetUs = sampleOffsetUs;
|
this.sampleOffsetUs = sampleOffsetUs;
|
||||||
this.mediaFormat = getAdjustedMediaFormat(mediaFormat, sampleOffsetUs);
|
this.adaptiveMaxWidth = adaptiveMaxWidth;
|
||||||
|
this.adaptiveMaxHeight = adaptiveMaxHeight;
|
||||||
|
this.mediaFormat = getAdjustedMediaFormat(mediaFormat, sampleOffsetUs, adaptiveMaxWidth,
|
||||||
|
adaptiveMaxHeight);
|
||||||
this.drmInitData = drmInitData;
|
this.drmInitData = drmInitData;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,7 +114,8 @@ public class ContainerMediaChunk extends BaseMediaChunk implements SingleTrackOu
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final void format(MediaFormat mediaFormat) {
|
public final void format(MediaFormat mediaFormat) {
|
||||||
this.mediaFormat = getAdjustedMediaFormat(mediaFormat, sampleOffsetUs);
|
this.mediaFormat = getAdjustedMediaFormat(mediaFormat, sampleOffsetUs, adaptiveMaxWidth,
|
||||||
|
adaptiveMaxHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -163,10 +175,16 @@ public class ContainerMediaChunk extends BaseMediaChunk implements SingleTrackOu
|
|||||||
|
|
||||||
// Private methods.
|
// Private methods.
|
||||||
|
|
||||||
private static MediaFormat getAdjustedMediaFormat(MediaFormat format, long sampleOffsetUs) {
|
private static MediaFormat getAdjustedMediaFormat(MediaFormat format, long sampleOffsetUs,
|
||||||
if (sampleOffsetUs != 0 && format != null
|
int adaptiveMaxWidth, int adaptiveMaxHeight) {
|
||||||
&& format.subsampleOffsetUs != MediaFormat.OFFSET_SAMPLE_RELATIVE) {
|
if (format == null) {
|
||||||
return format.copyWithSubsampleOffsetUs(format.subsampleOffsetUs + sampleOffsetUs);
|
return null;
|
||||||
|
}
|
||||||
|
if (sampleOffsetUs != 0 && format.subsampleOffsetUs != MediaFormat.OFFSET_SAMPLE_RELATIVE) {
|
||||||
|
format = format.copyWithSubsampleOffsetUs(format.subsampleOffsetUs + sampleOffsetUs);
|
||||||
|
}
|
||||||
|
if (adaptiveMaxWidth != MediaFormat.NO_VALUE || adaptiveMaxHeight != MediaFormat.NO_VALUE) {
|
||||||
|
format = format.copyWithMaxVideoDimensions(adaptiveMaxWidth, adaptiveMaxHeight);
|
||||||
}
|
}
|
||||||
return format;
|
return format;
|
||||||
}
|
}
|
||||||
|
@ -108,11 +108,6 @@ public final class MultiTrackChunkSource implements ChunkSource, ExoPlayerCompon
|
|||||||
selectedSource.maybeThrowError();
|
selectedSource.maybeThrowError();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public MediaFormat getWithMaxVideoDimensions(MediaFormat format) {
|
|
||||||
return selectedSource.getWithMaxVideoDimensions(format);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleMessage(int what, Object msg) throws ExoPlaybackException {
|
public void handleMessage(int what, Object msg) throws ExoPlaybackException {
|
||||||
Assertions.checkState(!enabled);
|
Assertions.checkState(!enabled);
|
||||||
|
@ -69,11 +69,6 @@ public final class SingleSampleChunkSource implements ChunkSource {
|
|||||||
return mediaFormat;
|
return mediaFormat;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public MediaFormat getWithMaxVideoDimensions(MediaFormat format) {
|
|
||||||
return format;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void enable(int track) {
|
public void enable(int track) {
|
||||||
// Do nothing.
|
// Do nothing.
|
||||||
|
@ -271,11 +271,10 @@ public class DashChunkSource implements ChunkSource {
|
|||||||
|
|
||||||
processManifest(currentManifest);
|
processManifest(currentManifest);
|
||||||
|
|
||||||
String mimeType = "";
|
|
||||||
long totalDurationUs = 0;
|
long totalDurationUs = 0;
|
||||||
int maxHeight = 0;
|
|
||||||
int maxWidth = 0;
|
int maxWidth = 0;
|
||||||
|
int maxHeight = 0;
|
||||||
|
String mimeType = "";
|
||||||
for (int i = 0; i < periodHolders.size(); i++) {
|
for (int i = 0; i < periodHolders.size(); i++) {
|
||||||
PeriodHolder periodHolder = periodHolders.valueAt(i);
|
PeriodHolder periodHolder = periodHolders.valueAt(i);
|
||||||
if (totalDurationUs != TrackRenderer.UNKNOWN_TIME_US) {
|
if (totalDurationUs != TrackRenderer.UNKNOWN_TIME_US) {
|
||||||
@ -285,21 +284,15 @@ public class DashChunkSource implements ChunkSource {
|
|||||||
totalDurationUs += periodHolder.durationUs;
|
totalDurationUs += periodHolder.durationUs;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mimeType = periodHolder.mimeType;
|
|
||||||
maxHeight = Math.max(maxHeight, periodHolder.maxHeight);
|
|
||||||
maxWidth = Math.max(maxWidth, periodHolder.maxWidth);
|
maxWidth = Math.max(maxWidth, periodHolder.maxWidth);
|
||||||
|
maxHeight = Math.max(maxHeight, periodHolder.maxHeight);
|
||||||
|
mimeType = periodHolder.mimeType;
|
||||||
}
|
}
|
||||||
|
this.maxWidth = maxWidth == 0 ? MediaFormat.NO_VALUE : maxWidth;
|
||||||
|
this.maxHeight = maxHeight == 0 ? MediaFormat.NO_VALUE : maxHeight;
|
||||||
// TODO: Remove this and pass proper formats instead (b/22996976).
|
// TODO: Remove this and pass proper formats instead (b/22996976).
|
||||||
this.mediaFormat = MediaFormat.createFormatForMimeType(mimeType, MediaFormat.NO_VALUE,
|
this.mediaFormat = MediaFormat.createFormatForMimeType(mimeType, MediaFormat.NO_VALUE,
|
||||||
totalDurationUs);
|
totalDurationUs);
|
||||||
this.maxHeight = maxHeight;
|
|
||||||
this.maxWidth = maxWidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final MediaFormat getWithMaxVideoDimensions(MediaFormat format) {
|
|
||||||
return MimeTypes.isVideo(mediaFormat.mimeType)
|
|
||||||
? format.copyWithMaxVideoDimensions(maxWidth, maxHeight) : format;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -606,8 +599,8 @@ public class DashChunkSource implements ChunkSource {
|
|||||||
boolean isMediaFormatFinal = (mediaFormat != null);
|
boolean isMediaFormatFinal = (mediaFormat != null);
|
||||||
return new ContainerMediaChunk(dataSource, dataSpec, trigger, representation.format,
|
return new ContainerMediaChunk(dataSource, dataSpec, trigger, representation.format,
|
||||||
startTimeUs, endTimeUs, segmentNum, isLastSegment, sampleOffsetUs,
|
startTimeUs, endTimeUs, segmentNum, isLastSegment, sampleOffsetUs,
|
||||||
representationHolder.extractorWrapper, mediaFormat, drmInitData, isMediaFormatFinal,
|
representationHolder.extractorWrapper, mediaFormat, maxWidth, maxHeight, drmInitData,
|
||||||
periodHolder.manifestIndex);
|
isMediaFormatFinal, periodHolder.manifestIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -854,8 +847,8 @@ public class DashChunkSource implements ChunkSource {
|
|||||||
formats = new Format[representationCount];
|
formats = new Format[representationCount];
|
||||||
representationHolders = new HashMap<>(representationCount);
|
representationHolders = new HashMap<>(representationCount);
|
||||||
|
|
||||||
int maxWidth = -1;
|
int maxWidth = 0;
|
||||||
int maxHeight = -1;
|
int maxHeight = 0;
|
||||||
String mimeType = "";
|
String mimeType = "";
|
||||||
for (int i = 0; i < representationCount; i++) {
|
for (int i = 0; i < representationCount; i++) {
|
||||||
int representationIndex = representationIndices != null ? representationIndices[i] : i;
|
int representationIndex = representationIndices != null ? representationIndices[i] : i;
|
||||||
|
@ -119,8 +119,8 @@ public class HlsChunkSource {
|
|||||||
private final BandwidthMeter bandwidthMeter;
|
private final BandwidthMeter bandwidthMeter;
|
||||||
private final int adaptiveMode;
|
private final int adaptiveMode;
|
||||||
private final String baseUri;
|
private final String baseUri;
|
||||||
private final int maxWidth;
|
private final int adaptiveMaxWidth;
|
||||||
private final int maxHeight;
|
private final int adaptiveMaxHeight;
|
||||||
private final long minBufferDurationToSwitchUpUs;
|
private final long minBufferDurationToSwitchUpUs;
|
||||||
private final long maxBufferDurationToSwitchDownUs;
|
private final long maxBufferDurationToSwitchDownUs;
|
||||||
|
|
||||||
@ -184,8 +184,8 @@ public class HlsChunkSource {
|
|||||||
variantBlacklistTimes = new long[1];
|
variantBlacklistTimes = new long[1];
|
||||||
setMediaPlaylist(0, (HlsMediaPlaylist) playlist);
|
setMediaPlaylist(0, (HlsMediaPlaylist) playlist);
|
||||||
// We won't be adapting between different variants.
|
// We won't be adapting between different variants.
|
||||||
maxWidth = -1;
|
adaptiveMaxWidth = MediaFormat.NO_VALUE;
|
||||||
maxHeight = -1;
|
adaptiveMaxHeight = MediaFormat.NO_VALUE;
|
||||||
} else {
|
} else {
|
||||||
List<Variant> masterPlaylistVariants = ((HlsMasterPlaylist) playlist).variants;
|
List<Variant> masterPlaylistVariants = ((HlsMasterPlaylist) playlist).variants;
|
||||||
variants = buildOrderedVariants(masterPlaylistVariants, variantIndices);
|
variants = buildOrderedVariants(masterPlaylistVariants, variantIndices);
|
||||||
@ -208,13 +208,13 @@ public class HlsChunkSource {
|
|||||||
}
|
}
|
||||||
if (variants.length <= 1 || adaptiveMode == ADAPTIVE_MODE_NONE) {
|
if (variants.length <= 1 || adaptiveMode == ADAPTIVE_MODE_NONE) {
|
||||||
// We won't be adapting between different variants.
|
// We won't be adapting between different variants.
|
||||||
this.maxWidth = -1;
|
this.adaptiveMaxWidth = MediaFormat.NO_VALUE;
|
||||||
this.maxHeight = -1;
|
this.adaptiveMaxHeight = MediaFormat.NO_VALUE;
|
||||||
} else {
|
} else {
|
||||||
// We will be adapting between different variants.
|
// We will be adapting between different variants.
|
||||||
// TODO: We should allow the default values to be passed through the constructor.
|
// TODO: We should allow the default values to be passed through the constructor.
|
||||||
this.maxWidth = maxWidth > 0 ? maxWidth : 1920;
|
this.adaptiveMaxWidth = maxWidth > 0 ? maxWidth : 1920;
|
||||||
this.maxHeight = maxHeight > 0 ? maxHeight : 1080;
|
this.adaptiveMaxHeight = maxHeight > 0 ? maxHeight : 1080;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -223,20 +223,6 @@ public class HlsChunkSource {
|
|||||||
return live ? C.UNKNOWN_TIME_US : durationUs;
|
return live ? C.UNKNOWN_TIME_US : durationUs;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Adaptive implementations must return a copy of the provided {@link MediaFormat} with the
|
|
||||||
* maximum video dimensions set. Other implementations can return the provided {@link MediaFormat}
|
|
||||||
* directly.
|
|
||||||
*
|
|
||||||
* @param format The format to be copied or returned.
|
|
||||||
* @return A copy of the provided {@link MediaFormat} with the maximum video dimensions set, or
|
|
||||||
* the provided format.
|
|
||||||
*/
|
|
||||||
public MediaFormat getWithMaxVideoDimensions(MediaFormat format) {
|
|
||||||
return (maxWidth == -1 || maxHeight == -1) ? format
|
|
||||||
: format.copyWithMaxVideoDimensions(maxWidth, maxHeight);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the next {@link Chunk} that should be loaded.
|
* Returns the next {@link Chunk} that should be loaded.
|
||||||
*
|
*
|
||||||
@ -348,7 +334,7 @@ public class HlsChunkSource {
|
|||||||
Extractor extractor = chunkUri.getLastPathSegment().endsWith(AAC_FILE_EXTENSION)
|
Extractor extractor = chunkUri.getLastPathSegment().endsWith(AAC_FILE_EXTENSION)
|
||||||
? new AdtsExtractor(startTimeUs) : new TsExtractor(startTimeUs);
|
? new AdtsExtractor(startTimeUs) : new TsExtractor(startTimeUs);
|
||||||
extractorWrapper = new HlsExtractorWrapper(trigger, format, startTimeUs, extractor,
|
extractorWrapper = new HlsExtractorWrapper(trigger, format, startTimeUs, extractor,
|
||||||
switchingVariantSpliced);
|
switchingVariantSpliced, adaptiveMaxWidth, adaptiveMaxHeight);
|
||||||
} else {
|
} else {
|
||||||
extractorWrapper = previousTsChunk.extractorWrapper;
|
extractorWrapper = previousTsChunk.extractorWrapper;
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,7 @@ import com.google.android.exoplayer.extractor.SeekMap;
|
|||||||
import com.google.android.exoplayer.extractor.TrackOutput;
|
import com.google.android.exoplayer.extractor.TrackOutput;
|
||||||
import com.google.android.exoplayer.upstream.Allocator;
|
import com.google.android.exoplayer.upstream.Allocator;
|
||||||
import com.google.android.exoplayer.util.Assertions;
|
import com.google.android.exoplayer.util.Assertions;
|
||||||
|
import com.google.android.exoplayer.util.MimeTypes;
|
||||||
|
|
||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
|
|
||||||
@ -44,7 +45,10 @@ public final class HlsExtractorWrapper implements ExtractorOutput {
|
|||||||
private final Extractor extractor;
|
private final Extractor extractor;
|
||||||
private final SparseArray<DefaultTrackOutput> sampleQueues;
|
private final SparseArray<DefaultTrackOutput> sampleQueues;
|
||||||
private final boolean shouldSpliceIn;
|
private final boolean shouldSpliceIn;
|
||||||
|
private final int adaptiveMaxWidth;
|
||||||
|
private final int adaptiveMaxHeight;
|
||||||
|
|
||||||
|
private MediaFormat[] sampleQueueFormats;
|
||||||
private Allocator allocator;
|
private Allocator allocator;
|
||||||
|
|
||||||
private volatile boolean tracksBuilt;
|
private volatile boolean tracksBuilt;
|
||||||
@ -54,12 +58,14 @@ public final class HlsExtractorWrapper implements ExtractorOutput {
|
|||||||
private boolean spliceConfigured;
|
private boolean spliceConfigured;
|
||||||
|
|
||||||
public HlsExtractorWrapper(int trigger, Format format, long startTimeUs, Extractor extractor,
|
public HlsExtractorWrapper(int trigger, Format format, long startTimeUs, Extractor extractor,
|
||||||
boolean shouldSpliceIn) {
|
boolean shouldSpliceIn, int adaptiveMaxWidth, int adaptiveMaxHeight) {
|
||||||
this.trigger = trigger;
|
this.trigger = trigger;
|
||||||
this.format = format;
|
this.format = format;
|
||||||
this.startTimeUs = startTimeUs;
|
this.startTimeUs = startTimeUs;
|
||||||
this.extractor = extractor;
|
this.extractor = extractor;
|
||||||
this.shouldSpliceIn = shouldSpliceIn;
|
this.shouldSpliceIn = shouldSpliceIn;
|
||||||
|
this.adaptiveMaxWidth = adaptiveMaxWidth;
|
||||||
|
this.adaptiveMaxHeight = adaptiveMaxHeight;
|
||||||
sampleQueues = new SparseArray<>();
|
sampleQueues = new SparseArray<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,6 +92,15 @@ public final class HlsExtractorWrapper implements ExtractorOutput {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
prepared = true;
|
prepared = true;
|
||||||
|
sampleQueueFormats = new MediaFormat[sampleQueues.size()];
|
||||||
|
for (int i = 0; i < sampleQueueFormats.length; i++) {
|
||||||
|
MediaFormat format = sampleQueues.valueAt(i).getFormat();
|
||||||
|
if (MimeTypes.isVideo(format.mimeType) && (adaptiveMaxWidth != MediaFormat.NO_VALUE
|
||||||
|
|| adaptiveMaxHeight != MediaFormat.NO_VALUE)) {
|
||||||
|
format = format.copyWithMaxVideoDimensions(adaptiveMaxWidth, adaptiveMaxHeight);
|
||||||
|
}
|
||||||
|
sampleQueueFormats[i] = format;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return prepared;
|
return prepared;
|
||||||
}
|
}
|
||||||
@ -169,7 +184,7 @@ public final class HlsExtractorWrapper implements ExtractorOutput {
|
|||||||
*/
|
*/
|
||||||
public MediaFormat getMediaFormat(int track) {
|
public MediaFormat getMediaFormat(int track) {
|
||||||
Assertions.checkState(isPrepared());
|
Assertions.checkState(isPrepared());
|
||||||
return sampleQueues.valueAt(track).getFormat();
|
return sampleQueueFormats[track];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -138,11 +138,11 @@ public final class HlsSampleSource implements SampleSource, SampleSourceReader,
|
|||||||
mediaFormats = new MediaFormat[trackCount];
|
mediaFormats = new MediaFormat[trackCount];
|
||||||
long durationUs = chunkSource.getDurationUs();
|
long durationUs = chunkSource.getDurationUs();
|
||||||
for (int i = 0; i < trackCount; i++) {
|
for (int i = 0; i < trackCount; i++) {
|
||||||
mediaFormats[i] = extractor.getMediaFormat(i).copyWithDurationUs(durationUs);
|
MediaFormat format = extractor.getMediaFormat(i).copyWithDurationUs(durationUs);
|
||||||
if (MimeTypes.isVideo(mediaFormats[i].mimeType)) {
|
if (MimeTypes.isVideo(format.mimeType)) {
|
||||||
mediaFormats[i] = chunkSource.getWithMaxVideoDimensions(mediaFormats[i])
|
format = format.copyAsAdaptive();
|
||||||
.copyAsAdaptive();
|
|
||||||
}
|
}
|
||||||
|
mediaFormats[i] = format;
|
||||||
}
|
}
|
||||||
prepared = true;
|
prepared = true;
|
||||||
return true;
|
return true;
|
||||||
@ -294,8 +294,7 @@ public final class HlsSampleSource implements SampleSource, SampleSourceReader,
|
|||||||
}
|
}
|
||||||
|
|
||||||
MediaFormat mediaFormat = extractor.getMediaFormat(track);
|
MediaFormat mediaFormat = extractor.getMediaFormat(track);
|
||||||
if (mediaFormat != null && !mediaFormat.equals(downstreamMediaFormats[track], true)) {
|
if (mediaFormat != null && !mediaFormat.equals(downstreamMediaFormats[track])) {
|
||||||
mediaFormat = chunkSource.getWithMaxVideoDimensions(mediaFormat);
|
|
||||||
formatHolder.format = mediaFormat;
|
formatHolder.format = mediaFormat;
|
||||||
downstreamMediaFormats[track] = mediaFormat;
|
downstreamMediaFormats[track] = mediaFormat;
|
||||||
return FORMAT_READ;
|
return FORMAT_READ;
|
||||||
|
@ -196,15 +196,6 @@ public class SmoothStreamingChunkSource implements ChunkSource,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public final MediaFormat getWithMaxVideoDimensions(MediaFormat format) {
|
|
||||||
if (enabledTrack.isAdaptive() && MimeTypes.isVideo(format.mimeType)) {
|
|
||||||
return format.copyWithMaxVideoDimensions(
|
|
||||||
enabledTrack.adaptiveMaxWidth, enabledTrack.adaptiveMaxHeight);
|
|
||||||
}
|
|
||||||
return format;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void continueBuffering(long playbackPositionUs) {
|
public void continueBuffering(long playbackPositionUs) {
|
||||||
if (manifestFetcher == null || !currentManifest.isLive || fatalError != null) {
|
if (manifestFetcher == null || !currentManifest.isLive || fatalError != null) {
|
||||||
@ -323,10 +314,10 @@ public class SmoothStreamingChunkSource implements ChunkSource,
|
|||||||
int manifestTrackKey = getManifestTrackKey(enabledTrack.elementIndex, manifestTrackIndex);
|
int manifestTrackKey = getManifestTrackKey(enabledTrack.elementIndex, manifestTrackIndex);
|
||||||
Uri uri = streamElement.buildRequestUri(manifestTrackIndex, chunkIndex);
|
Uri uri = streamElement.buildRequestUri(manifestTrackIndex, chunkIndex);
|
||||||
Chunk mediaChunk = newMediaChunk(selectedFormat, uri, null,
|
Chunk mediaChunk = newMediaChunk(selectedFormat, uri, null,
|
||||||
extractorWrappers.get(manifestTrackKey),
|
extractorWrappers.get(manifestTrackKey), drmInitData, dataSource, currentAbsoluteChunkIndex,
|
||||||
drmInitData, dataSource, currentAbsoluteChunkIndex, isLastChunk, chunkStartTimeUs,
|
isLastChunk, chunkStartTimeUs, chunkEndTimeUs, evaluation.trigger,
|
||||||
chunkEndTimeUs, evaluation.trigger,
|
mediaFormats.get(manifestTrackKey), enabledTrack.adaptiveMaxWidth,
|
||||||
mediaFormats.get(manifestTrackKey));
|
enabledTrack.adaptiveMaxHeight);
|
||||||
out.chunk = mediaChunk;
|
out.chunk = mediaChunk;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -480,14 +471,14 @@ public class SmoothStreamingChunkSource implements ChunkSource,
|
|||||||
private static MediaChunk newMediaChunk(Format formatInfo, Uri uri, String cacheKey,
|
private static MediaChunk newMediaChunk(Format formatInfo, Uri uri, String cacheKey,
|
||||||
ChunkExtractorWrapper extractorWrapper, DrmInitData drmInitData, DataSource dataSource,
|
ChunkExtractorWrapper extractorWrapper, DrmInitData drmInitData, DataSource dataSource,
|
||||||
int chunkIndex, boolean isLast, long chunkStartTimeUs, long chunkEndTimeUs,
|
int chunkIndex, boolean isLast, long chunkStartTimeUs, long chunkEndTimeUs,
|
||||||
int trigger, MediaFormat mediaFormat) {
|
int trigger, MediaFormat mediaFormat, int adaptiveMaxWidth, int adaptiveMaxHeight) {
|
||||||
long offset = 0;
|
long offset = 0;
|
||||||
DataSpec dataSpec = new DataSpec(uri, offset, -1, cacheKey);
|
DataSpec dataSpec = new DataSpec(uri, offset, -1, cacheKey);
|
||||||
// In SmoothStreaming each chunk contains sample timestamps relative to the start of the chunk.
|
// In SmoothStreaming each chunk contains sample timestamps relative to the start of the chunk.
|
||||||
// To convert them the absolute timestamps, we need to set sampleOffsetUs to -chunkStartTimeUs.
|
// To convert them the absolute timestamps, we need to set sampleOffsetUs to -chunkStartTimeUs.
|
||||||
return new ContainerMediaChunk(dataSource, dataSpec, trigger, formatInfo, chunkStartTimeUs,
|
return new ContainerMediaChunk(dataSource, dataSpec, trigger, formatInfo, chunkStartTimeUs,
|
||||||
chunkEndTimeUs, chunkIndex, isLast, chunkStartTimeUs, extractorWrapper, mediaFormat,
|
chunkEndTimeUs, chunkIndex, isLast, chunkStartTimeUs, extractorWrapper, mediaFormat,
|
||||||
drmInitData, true, Chunk.NO_PARENT_ID);
|
adaptiveMaxWidth, adaptiveMaxHeight, drmInitData, true, Chunk.NO_PARENT_ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int getManifestTrackKey(int elementIndex, int trackIndex) {
|
private static int getManifestTrackKey(int elementIndex, int trackIndex) {
|
||||||
@ -538,17 +529,17 @@ public class SmoothStreamingChunkSource implements ChunkSource,
|
|||||||
this.elementIndex = elementIndex;
|
this.elementIndex = elementIndex;
|
||||||
this.fixedFormat = fixedFormat;
|
this.fixedFormat = fixedFormat;
|
||||||
this.adaptiveFormats = null;
|
this.adaptiveFormats = null;
|
||||||
this.adaptiveMaxWidth = -1;
|
this.adaptiveMaxWidth = MediaFormat.NO_VALUE;
|
||||||
this.adaptiveMaxHeight = -1;
|
this.adaptiveMaxHeight = MediaFormat.NO_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ExposedTrack(MediaFormat format, int elementIndex, Format[] adaptiveFormats,
|
public ExposedTrack(MediaFormat format, int elementIndex, Format[] adaptiveFormats,
|
||||||
int maxWidth, int maxHeight) {
|
int adaptiveMaxWidth, int adaptiveMaxHeight) {
|
||||||
this.format = format;
|
this.format = format;
|
||||||
this.elementIndex = elementIndex;
|
this.elementIndex = elementIndex;
|
||||||
this.adaptiveFormats = adaptiveFormats;
|
this.adaptiveFormats = adaptiveFormats;
|
||||||
this.adaptiveMaxWidth = maxWidth;
|
this.adaptiveMaxWidth = adaptiveMaxWidth;
|
||||||
this.adaptiveMaxHeight = maxHeight;
|
this.adaptiveMaxHeight = adaptiveMaxHeight;
|
||||||
this.fixedFormat = null;
|
this.fixedFormat = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user