From 90f9f7314d0125106c558458f5ca6e90dd67473e Mon Sep 17 00:00:00 2001 From: Oliver Woodman Date: Mon, 17 Aug 2015 17:11:37 +0100 Subject: [PATCH] Allow MediaFormat to express that it's adaptive (not yet used). Also: - Better document MediaFormat. - Document getFormat to indicate what it's good (and not good) for. --- .../exoplayer/FrameworkSampleSource.java | 2 +- .../google/android/exoplayer/MediaFormat.java | 115 ++++++++++++++---- .../android/exoplayer/SampleSource.java | 7 ++ 3 files changed, 100 insertions(+), 24 deletions(-) diff --git a/library/src/main/java/com/google/android/exoplayer/FrameworkSampleSource.java b/library/src/main/java/com/google/android/exoplayer/FrameworkSampleSource.java index 6f485c75e4..582deca1df 100644 --- a/library/src/main/java/com/google/android/exoplayer/FrameworkSampleSource.java +++ b/library/src/main/java/com/google/android/exoplayer/FrameworkSampleSource.java @@ -317,7 +317,7 @@ public final class FrameworkSampleSource implements SampleSource, SampleSourceRe ? format.getLong(android.media.MediaFormat.KEY_DURATION) : C.UNKNOWN_TIME_US; return new MediaFormat(mimeType, maxInputSize, durationUs, width, height, rotationDegrees, MediaFormat.NO_VALUE, channelCount, sampleRate, language, initializationData, - MediaFormat.NO_VALUE, MediaFormat.NO_VALUE); + false, MediaFormat.NO_VALUE, MediaFormat.NO_VALUE); } @TargetApi(16) diff --git a/library/src/main/java/com/google/android/exoplayer/MediaFormat.java b/library/src/main/java/com/google/android/exoplayer/MediaFormat.java index b007742205..d5f7625d11 100644 --- a/library/src/main/java/com/google/android/exoplayer/MediaFormat.java +++ b/library/src/main/java/com/google/android/exoplayer/MediaFormat.java @@ -15,6 +15,7 @@ */ package com.google.android.exoplayer; +import com.google.android.exoplayer.util.Assertions; import com.google.android.exoplayer.util.Util; import android.annotation.SuppressLint; @@ -32,34 +33,88 @@ public final class MediaFormat { public static final int NO_VALUE = -1; + /** + * The mime type of the format. + */ public final String mimeType; + /** + * The maximum size of a buffer of data (typically one sample) in the format, or {@link #NO_VALUE} + * if unknown or not applicable. + */ public final int maxInputSize; - /** * The duration in microseconds, or {@link C#UNKNOWN_TIME_US} if the duration is unknown, or * {@link C#MATCH_LONGEST_US} if the duration should match the duration of the longest track whose * duration is known. */ public final long durationUs; + /** + * Initialization data that must be provided to the decoder. Will not be null, but may be empty + * if initialization data is not required. + */ + public final List initializationData; + /** + * Whether the format represents an adaptive track, meaning that the format of the actual media + * data may change (e.g. to adapt to network conditions). + */ + public final boolean adaptive; + // Video specific. + + /** + * The width of the video in pixels, or {@link #NO_VALUE} if unknown or not applicable. + */ public final int width; + + /** + * The height of the video in pixels, or {@link #NO_VALUE} if unknown or not applicable. + */ public final int height; + /** + * For formats that belong to an adaptive video track (either describing the track, or describing + * a specific format within it), this is the maximum width of the video in pixels that will be + * encountered in the stream. Set to {@link #NO_VALUE} if unknown or not applicable. + */ + public final int maxWidth; + /** + * For formats that belong to an adaptive video track (either describing the track, or describing + * a specific format within it), this is the maximum height of the video in pixels that will be + * encountered in the stream. Set to {@link #NO_VALUE} if unknown or not applicable. + */ + public final int maxHeight; + /** + * The clockwise rotation that should be applied to the video for it to be rendered in the correct + * orientation, or {@link #NO_VALUE} if unknown or not applicable. Only 0, 90, 180 and 270 are + * supported. + */ public final int rotationDegrees; + /** + * The width to height ratio of pixels in the video, or {@link #NO_VALUE} if unknown or not + * applicable. + */ public final float pixelWidthHeightRatio; + // Audio specific. + + /** + * The number of audio channels, or {@link #NO_VALUE} if unknown or not applicable. + */ public final int channelCount; + /** + * The audio sampling rate in Hz, or {@link #NO_VALUE} if unknown or not applicable. + */ public final int sampleRate; + // Text specific. + + /** + * The language of the track, or null if unknown or not applicable. + */ public final String language; - public final List initializationData; + // Lazy-initialized hashcode and framework media format. - public final int maxWidth; - public final int maxHeight; - - // Lazy-initialized hashcode. private int hashCode; - // Possibly-lazy-initialized framework media format. private android.media.MediaFormat frameworkMediaFormat; public static MediaFormat createVideoFormat(String mimeType, int maxInputSize, int width, @@ -79,19 +134,21 @@ public final class MediaFormat { int width, int height, int rotationDegrees, float pixelWidthHeightRatio, List initializationData) { return new MediaFormat(mimeType, maxInputSize, durationUs, width, height, rotationDegrees, - pixelWidthHeightRatio, NO_VALUE, NO_VALUE, null, initializationData, NO_VALUE, NO_VALUE); + pixelWidthHeightRatio, NO_VALUE, NO_VALUE, null, initializationData, false, NO_VALUE, + NO_VALUE); } public static MediaFormat createAudioFormat(String mimeType, int maxInputSize, int channelCount, int sampleRate, List initializationData) { - return createAudioFormat( - mimeType, maxInputSize, C.UNKNOWN_TIME_US, channelCount, sampleRate, initializationData); + return createAudioFormat(mimeType, maxInputSize, C.UNKNOWN_TIME_US, channelCount, sampleRate, + initializationData); } public static MediaFormat createAudioFormat(String mimeType, int maxInputSize, long durationUs, int channelCount, int sampleRate, List initializationData) { return new MediaFormat(mimeType, maxInputSize, durationUs, NO_VALUE, NO_VALUE, NO_VALUE, - NO_VALUE, channelCount, sampleRate, null, initializationData, NO_VALUE, NO_VALUE); + NO_VALUE, channelCount, sampleRate, null, initializationData, false, NO_VALUE, + NO_VALUE); } public static MediaFormat createTextFormat(String mimeType, String language) { @@ -100,7 +157,7 @@ public final class MediaFormat { public static MediaFormat createTextFormat(String mimeType, String language, long durationUs) { return new MediaFormat(mimeType, NO_VALUE, durationUs, NO_VALUE, NO_VALUE, NO_VALUE, - NO_VALUE, NO_VALUE, NO_VALUE, language, null, NO_VALUE, NO_VALUE); + NO_VALUE, NO_VALUE, NO_VALUE, language, null, false, NO_VALUE, NO_VALUE); } public static MediaFormat createFormatForMimeType(String mimeType) { @@ -109,14 +166,23 @@ public final class MediaFormat { public static MediaFormat createFormatForMimeType(String mimeType, long durationUs) { return new MediaFormat(mimeType, NO_VALUE, durationUs, NO_VALUE, NO_VALUE, NO_VALUE, - NO_VALUE, NO_VALUE, NO_VALUE, null, null, NO_VALUE, NO_VALUE); + NO_VALUE, NO_VALUE, NO_VALUE, null, null, false, NO_VALUE, NO_VALUE); + } + + public static MediaFormat createAdaptiveFormat(String mimeType) { + return createAdaptiveFormat(mimeType, C.UNKNOWN_TIME_US); + } + + public static MediaFormat createAdaptiveFormat(String mimeType, long durationUs) { + return new MediaFormat(mimeType, NO_VALUE, durationUs, NO_VALUE, NO_VALUE, NO_VALUE, + NO_VALUE, NO_VALUE, NO_VALUE, null, null, true, NO_VALUE, NO_VALUE); } /* package */ MediaFormat(String mimeType, int maxInputSize, long durationUs, int width, int height, int rotationDegrees, float pixelWidthHeightRatio, int channelCount, - int sampleRate, String language, List initializationData, int maxWidth, - int maxHeight) { - this.mimeType = mimeType; + int sampleRate, String language, List initializationData, boolean adaptive, + int maxWidth, int maxHeight) { + this.mimeType = Assertions.checkNotEmpty(mimeType); this.maxInputSize = maxInputSize; this.durationUs = durationUs; this.width = width; @@ -128,20 +194,21 @@ public final class MediaFormat { this.language = language; this.initializationData = initializationData == null ? Collections.emptyList() : initializationData; + this.adaptive = adaptive; this.maxWidth = maxWidth; this.maxHeight = maxHeight; } public MediaFormat copyWithMaxVideoDimension(int maxWidth, int maxHeight) { return new MediaFormat(mimeType, maxInputSize, durationUs, width, height, rotationDegrees, - pixelWidthHeightRatio, channelCount, sampleRate, language, initializationData, maxWidth, - maxHeight); + pixelWidthHeightRatio, channelCount, sampleRate, language, initializationData, false, + maxWidth, maxHeight); } public MediaFormat copyWithDurationUs(long durationUs) { return new MediaFormat(mimeType, maxInputSize, durationUs, width, height, rotationDegrees, - pixelWidthHeightRatio, channelCount, sampleRate, language, initializationData, maxWidth, - maxHeight); + pixelWidthHeightRatio, channelCount, sampleRate, language, initializationData, false, + maxWidth, maxHeight); } /** @@ -177,7 +244,8 @@ public final class MediaFormat { public String toString() { return "MediaFormat(" + mimeType + ", " + maxInputSize + ", " + width + ", " + height + ", " + rotationDegrees + ", " + pixelWidthHeightRatio + ", " + channelCount + ", " + sampleRate - + ", " + language + ", " + durationUs + ", " + maxWidth + ", " + maxHeight + ")"; + + ", " + language + ", " + durationUs + ", " + adaptive + ", " + maxWidth + ", " + maxHeight + + ")"; } @Override @@ -191,6 +259,7 @@ public final class MediaFormat { result = 31 * result + rotationDegrees; result = 31 * result + Float.floatToRawIntBits(pixelWidthHeightRatio); result = 31 * result + (int) durationUs; + result = 31 * result + (adaptive ? 1231 : 1237); result = 31 * result + maxWidth; result = 31 * result + maxHeight; result = 31 * result + channelCount; @@ -226,8 +295,8 @@ public final class MediaFormat { } private boolean equalsInternal(MediaFormat other, boolean ignoreMaxDimensions) { - if (maxInputSize != other.maxInputSize || width != other.width || height != other.height - || rotationDegrees != other.rotationDegrees + if (adaptive != other.adaptive || maxInputSize != other.maxInputSize || width != other.width + || height != other.height || rotationDegrees != other.rotationDegrees || pixelWidthHeightRatio != other.pixelWidthHeightRatio || (!ignoreMaxDimensions && (maxWidth != other.maxWidth || maxHeight != other.maxHeight)) || channelCount != other.channelCount || sampleRate != other.sampleRate diff --git a/library/src/main/java/com/google/android/exoplayer/SampleSource.java b/library/src/main/java/com/google/android/exoplayer/SampleSource.java index b33d18c5cb..e42cea77d6 100644 --- a/library/src/main/java/com/google/android/exoplayer/SampleSource.java +++ b/library/src/main/java/com/google/android/exoplayer/SampleSource.java @@ -89,6 +89,13 @@ public interface SampleSource { /** * Returns the format of the specified track. *

+ * Note that whilst the format of a track will remain constant, the format of the actual media + * stream may change dynamically. An example of this is where the track is adaptive + * (i.e. @link {@link MediaFormat#adaptive} is true). Hence the track formats returned through + * this method should not be used to configure decoders. Decoder configuration should be + * performed using the formats obtained when reading the media stream through calls to + * {@link #readData(int, long, MediaFormatHolder, SampleHolder, boolean)}. + *

* This method should not be called until after the source has been successfully prepared. * * @param track The track index.