diff --git a/libraries/common/src/main/java/androidx/media3/common/C.java b/libraries/common/src/main/java/androidx/media3/common/C.java
index 411c0e3451e..594adc7ca0f 100644
--- a/libraries/common/src/main/java/androidx/media3/common/C.java
+++ b/libraries/common/src/main/java/androidx/media3/common/C.java
@@ -168,10 +168,10 @@ private C() {}
* {@link #ENCODING_INVALID}, {@link #ENCODING_PCM_8BIT}, {@link #ENCODING_PCM_16BIT}, {@link
* #ENCODING_PCM_16BIT_BIG_ENDIAN}, {@link #ENCODING_PCM_24BIT}, {@link
* #ENCODING_PCM_24BIT_BIG_ENDIAN}, {@link #ENCODING_PCM_32BIT}, {@link
- * #ENCODING_PCM_32BIT_BIG_ENDIAN}, {@link #ENCODING_PCM_FLOAT}, {@link #ENCODING_MP3}, {@link
- * #ENCODING_AC3}, {@link #ENCODING_E_AC3}, {@link #ENCODING_E_AC3_JOC}, {@link #ENCODING_AC4},
- * {@link #ENCODING_DTS}, {@link #ENCODING_DTS_HD}, {@link #ENCODING_DOLBY_TRUEHD} or {@link
- * #ENCODING_OPUS}.
+ * #ENCODING_PCM_32BIT_BIG_ENDIAN}, {@link #ENCODING_PCM_FLOAT}, {@link #ENCODING_PCM_DOUBLE},
+ * {@link #ENCODING_MP3}, {@link #ENCODING_AC3}, {@link #ENCODING_E_AC3}, {@link
+ * #ENCODING_E_AC3_JOC}, {@link #ENCODING_AC4}, {@link #ENCODING_DTS}, {@link #ENCODING_DTS_HD},
+ * {@link #ENCODING_DOLBY_TRUEHD}, {@link #ENCODING_OPUS} or {@link #ENCODING_DSD}.
*/
@UnstableApi
@Documented
@@ -188,6 +188,7 @@ private C() {}
ENCODING_PCM_32BIT,
ENCODING_PCM_32BIT_BIG_ENDIAN,
ENCODING_PCM_FLOAT,
+ ENCODING_PCM_DOUBLE,
ENCODING_MP3,
ENCODING_AAC_LC,
ENCODING_AAC_HE_V1,
@@ -204,6 +205,7 @@ private C() {}
ENCODING_DOLBY_TRUEHD,
ENCODING_OPUS,
ENCODING_DTS_UHD_P2,
+ ENCODING_DSD,
})
public @interface Encoding {}
@@ -212,7 +214,7 @@ private C() {}
* {@link #ENCODING_INVALID}, {@link #ENCODING_PCM_8BIT}, {@link #ENCODING_PCM_16BIT}, {@link
* #ENCODING_PCM_16BIT_BIG_ENDIAN}, {@link #ENCODING_PCM_24BIT}, {@link
* #ENCODING_PCM_24BIT_BIG_ENDIAN}, {@link #ENCODING_PCM_32BIT}, {@link
- * #ENCODING_PCM_32BIT_BIG_ENDIAN}, {@link #ENCODING_PCM_FLOAT}.
+ * #ENCODING_PCM_32BIT_BIG_ENDIAN}, {@link #ENCODING_PCM_FLOAT}, {@link #ENCODING_PCM_DOUBLE}.
*/
@UnstableApi
@Documented
@@ -228,7 +230,8 @@ private C() {}
ENCODING_PCM_24BIT_BIG_ENDIAN,
ENCODING_PCM_32BIT,
ENCODING_PCM_32BIT_BIG_ENDIAN,
- ENCODING_PCM_FLOAT
+ ENCODING_PCM_FLOAT,
+ ENCODING_PCM_DOUBLE
})
public @interface PcmEncoding {}
@@ -259,6 +262,9 @@ private C() {}
/** See {@link AudioFormat#ENCODING_PCM_FLOAT}. */
@UnstableApi public static final int ENCODING_PCM_FLOAT = AudioFormat.ENCODING_PCM_FLOAT;
+ /** PCM encoding with double-precision floating point samples. */
+ @UnstableApi public static final int ENCODING_PCM_DOUBLE = 0x70000000;
+
/** See {@link AudioFormat#ENCODING_MP3}. */
@UnstableApi public static final int ENCODING_MP3 = AudioFormat.ENCODING_MP3;
@@ -307,6 +313,9 @@ private C() {}
/** See {@link AudioFormat#ENCODING_OPUS}. */
@UnstableApi public static final int ENCODING_OPUS = AudioFormat.ENCODING_OPUS;
+ /** See {@link AudioFormat#ENCODING_DSD}. */
+ @UnstableApi public static final int ENCODING_DSD = AudioFormat.ENCODING_DSD;
+
/**
* Represents the behavior affecting whether spatialization will be used. One of {@link
* #SPATIALIZATION_BEHAVIOR_AUTO} or {@link #SPATIALIZATION_BEHAVIOR_NEVER}.
diff --git a/libraries/common/src/main/java/androidx/media3/common/MimeTypes.java b/libraries/common/src/main/java/androidx/media3/common/MimeTypes.java
index 1c1e0524d1a..120f314e866 100644
--- a/libraries/common/src/main/java/androidx/media3/common/MimeTypes.java
+++ b/libraries/common/src/main/java/androidx/media3/common/MimeTypes.java
@@ -86,6 +86,7 @@ public final class MimeTypes {
public static final String AUDIO_E_AC3_JOC = BASE_TYPE_AUDIO + "/eac3-joc";
public static final String AUDIO_AC4 = BASE_TYPE_AUDIO + "/ac4";
public static final String AUDIO_TRUEHD = BASE_TYPE_AUDIO + "/true-hd";
+ public static final String AUDIO_DSD = BASE_TYPE_AUDIO + "/x-dsd";
public static final String AUDIO_DTS = BASE_TYPE_AUDIO + "/vnd.dts";
public static final String AUDIO_DTS_HD = BASE_TYPE_AUDIO + "/vnd.dts.hd";
public static final String AUDIO_DTS_EXPRESS = BASE_TYPE_AUDIO + "/vnd.dts.hd;profile=lbr";
@@ -686,6 +687,8 @@ public static boolean isDolbyVisionCodec(
return C.ENCODING_DOLBY_TRUEHD;
case MimeTypes.AUDIO_OPUS:
return C.ENCODING_OPUS;
+ case MimeTypes.AUDIO_DSD:
+ return C.ENCODING_DSD;
default:
return C.ENCODING_INVALID;
}
diff --git a/libraries/common/src/main/java/androidx/media3/common/audio/ToInt16PcmAudioProcessor.java b/libraries/common/src/main/java/androidx/media3/common/audio/ToInt16PcmAudioProcessor.java
index 867805e7413..789956d9ae8 100644
--- a/libraries/common/src/main/java/androidx/media3/common/audio/ToInt16PcmAudioProcessor.java
+++ b/libraries/common/src/main/java/androidx/media3/common/audio/ToInt16PcmAudioProcessor.java
@@ -34,6 +34,7 @@
*
{@link C#ENCODING_PCM_32BIT}
* {@link C#ENCODING_PCM_32BIT_BIG_ENDIAN}
* {@link C#ENCODING_PCM_FLOAT}
+ * {@link C#ENCODING_PCM_DOUBLE}
*
*/
@UnstableApi
@@ -50,7 +51,8 @@ public AudioFormat onConfigure(AudioFormat inputAudioFormat)
&& encoding != C.ENCODING_PCM_24BIT_BIG_ENDIAN
&& encoding != C.ENCODING_PCM_32BIT
&& encoding != C.ENCODING_PCM_32BIT_BIG_ENDIAN
- && encoding != C.ENCODING_PCM_FLOAT) {
+ && encoding != C.ENCODING_PCM_FLOAT
+ && encoding != C.ENCODING_PCM_DOUBLE) {
throw new UnhandledAudioFormatException(inputAudioFormat);
}
return encoding != C.ENCODING_PCM_16BIT
@@ -82,6 +84,9 @@ public void queueInput(ByteBuffer inputBuffer) {
case C.ENCODING_PCM_FLOAT:
resampledSize = size / 2;
break;
+ case C.ENCODING_PCM_DOUBLE:
+ resampledSize = size / 4;
+ break;
case C.ENCODING_PCM_16BIT:
case C.ENCODING_INVALID:
case Format.NO_VALUE:
@@ -147,6 +152,19 @@ public void queueInput(ByteBuffer inputBuffer) {
buffer.put((byte) ((shortValue >> 8) & 0xFF));
}
break;
+ case C.ENCODING_PCM_DOUBLE:
+ // 64 bit floating point -> 16 bit resampling. Floating point values are in the range
+ // [-1.0, 1.0], so need to be scaled by Short.MAX_VALUE.
+ for (int i = position; i < limit; i += 8) {
+ // Clamp to avoid integer overflow if the floating point values exceed their nominal range
+ // [Internal ref: b/161204847].
+ double doubleValue =
+ Util.constrainValue(inputBuffer.getDouble(i), /* min= */ -1, /* max= */ 1);
+ short shortValue = (short) (doubleValue * Short.MAX_VALUE);
+ buffer.put((byte) (shortValue & 0xFF));
+ buffer.put((byte) ((shortValue >> 8) & 0xFF));
+ }
+ break;
case C.ENCODING_PCM_16BIT:
case C.ENCODING_INVALID:
case Format.NO_VALUE:
diff --git a/libraries/common/src/main/java/androidx/media3/common/util/MediaFormatUtil.java b/libraries/common/src/main/java/androidx/media3/common/util/MediaFormatUtil.java
index 40289d9e2fa..8f4831f0bc1 100644
--- a/libraries/common/src/main/java/androidx/media3/common/util/MediaFormatUtil.java
+++ b/libraries/common/src/main/java/androidx/media3/common/util/MediaFormatUtil.java
@@ -482,6 +482,7 @@ private static void maybeSetPcmEncoding(
case C.ENCODING_PCM_16BIT_BIG_ENDIAN:
case C.ENCODING_PCM_24BIT_BIG_ENDIAN:
case C.ENCODING_PCM_32BIT_BIG_ENDIAN:
+ case C.ENCODING_PCM_DOUBLE:
default:
// No matching value. Do nothing.
return;
diff --git a/libraries/common/src/main/java/androidx/media3/common/util/Util.java b/libraries/common/src/main/java/androidx/media3/common/util/Util.java
index 3319921aee5..4c56141a94e 100644
--- a/libraries/common/src/main/java/androidx/media3/common/util/Util.java
+++ b/libraries/common/src/main/java/androidx/media3/common/util/Util.java
@@ -1188,6 +1188,19 @@ public static float constrainValue(float value, float min, float max) {
return max(min, min(value, max));
}
+ /**
+ * Constrains a value to the specified bounds.
+ *
+ * @param value The value to constrain.
+ * @param min The lower bound.
+ * @param max The upper bound.
+ * @return The constrained value {@code Math.max(min, Math.min(value, max))}.
+ */
+ @UnstableApi
+ public static double constrainValue(double value, double min, double max) {
+ return max(min, min(value, max));
+ }
+
/**
* Returns the sum of two arguments, or a third argument if the result overflows.
*
@@ -2258,7 +2271,8 @@ public static boolean isEncodingLinearPcm(@C.Encoding int encoding) {
|| encoding == C.ENCODING_PCM_24BIT_BIG_ENDIAN
|| encoding == C.ENCODING_PCM_32BIT
|| encoding == C.ENCODING_PCM_32BIT_BIG_ENDIAN
- || encoding == C.ENCODING_PCM_FLOAT;
+ || encoding == C.ENCODING_PCM_FLOAT
+ || encoding == C.ENCODING_PCM_DOUBLE;
}
/**
@@ -2273,7 +2287,8 @@ public static boolean isEncodingHighResolutionPcm(@C.PcmEncoding int encoding) {
|| encoding == C.ENCODING_PCM_24BIT_BIG_ENDIAN
|| encoding == C.ENCODING_PCM_32BIT
|| encoding == C.ENCODING_PCM_32BIT_BIG_ENDIAN
- || encoding == C.ENCODING_PCM_FLOAT;
+ || encoding == C.ENCODING_PCM_FLOAT
+ || encoding == C.ENCODING_PCM_DOUBLE;
}
/**
@@ -2383,6 +2398,7 @@ public static int getApiLevelThatAudioFormatIntroducedAudioEncoding(int encoding
case C.ENCODING_PCM_32BIT:
return 31;
case C.ENCODING_DTS_UHD_P2:
+ case C.ENCODING_DSD:
return 34;
default:
return Integer.MAX_VALUE;
@@ -2422,6 +2438,8 @@ public static int getByteDepth(@C.PcmEncoding int pcmEncoding) {
case C.ENCODING_PCM_32BIT_BIG_ENDIAN:
case C.ENCODING_PCM_FLOAT:
return 4;
+ case C.ENCODING_PCM_DOUBLE:
+ return 8;
case C.ENCODING_INVALID:
case Format.NO_VALUE:
default:
diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/ChannelMappingAudioProcessor.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/ChannelMappingAudioProcessor.java
index 10878714489..d34446a95c6 100644
--- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/ChannelMappingAudioProcessor.java
+++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/ChannelMappingAudioProcessor.java
@@ -113,6 +113,9 @@ public void queueInput(ByteBuffer inputBuffer) {
case C.ENCODING_PCM_FLOAT:
buffer.putFloat(inputBuffer.getFloat(inputIndex));
break;
+ case C.ENCODING_PCM_DOUBLE:
+ buffer.putDouble(inputBuffer.getDouble(inputIndex));
+ break;
default:
throw new IllegalStateException("Unexpected encoding: " + inputAudioFormat.encoding);
}
diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DefaultAudioSink.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DefaultAudioSink.java
index 8b77e0700e4..8dc295bebeb 100644
--- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DefaultAudioSink.java
+++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DefaultAudioSink.java
@@ -1923,7 +1923,9 @@ private static int getFramesPerEncodedSample(@C.Encoding int encoding, ByteBuffe
case C.ENCODING_PCM_32BIT_BIG_ENDIAN:
case C.ENCODING_PCM_8BIT:
case C.ENCODING_PCM_FLOAT:
+ case C.ENCODING_PCM_DOUBLE:
case C.ENCODING_AAC_ER_BSAC:
+ case C.ENCODING_DSD:
case C.ENCODING_INVALID:
case Format.NO_VALUE:
default:
diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/PcmAudioUtil.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/PcmAudioUtil.java
index 661be34de3c..7a597039f36 100644
--- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/PcmAudioUtil.java
+++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/PcmAudioUtil.java
@@ -101,6 +101,13 @@ public static int readAs32BitIntPcm(ByteBuffer buffer, @C.Encoding int pcmEncodi
} else {
return (int) (floatValue * Integer.MAX_VALUE);
}
+ case C.ENCODING_PCM_DOUBLE:
+ double doubleValue = Util.constrainValue(buffer.getDouble(), /* min= */ -1f, /* max= */ 1f);
+ if (doubleValue < 0) {
+ return (int) (-doubleValue * Integer.MIN_VALUE);
+ } else {
+ return (int) (doubleValue * Integer.MAX_VALUE);
+ }
default:
throw new IllegalStateException();
}
@@ -156,6 +163,13 @@ public static void write32BitIntPcm(
buffer.putFloat((float) pcm32bit / Integer.MAX_VALUE);
}
return;
+ case C.ENCODING_PCM_DOUBLE:
+ if (pcm32bit < 0) {
+ buffer.putDouble(-((double) pcm32bit) / Integer.MIN_VALUE);
+ } else {
+ buffer.putDouble((double) pcm32bit / Integer.MAX_VALUE);
+ }
+ return;
default:
throw new IllegalStateException();
}
diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/ToFloatPcmAudioProcessor.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/ToFloatPcmAudioProcessor.java
index c5aaaeb562d..36bd9f453a0 100644
--- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/ToFloatPcmAudioProcessor.java
+++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/ToFloatPcmAudioProcessor.java
@@ -33,6 +33,7 @@
* {@link C#ENCODING_PCM_32BIT}
* {@link C#ENCODING_PCM_32BIT_BIG_ENDIAN}
* {@link C#ENCODING_PCM_FLOAT} ({@link #isActive()} will return {@code false})
+ * {@link C#ENCODING_PCM_DOUBLE}
*
*/
@UnstableApi
@@ -104,6 +105,12 @@ public void queueInput(ByteBuffer inputBuffer) {
writePcm32BitFloat(pcm32BitInteger, buffer);
}
break;
+ case C.ENCODING_PCM_DOUBLE:
+ buffer = replaceOutputBuffer(size / 2);
+ for (int i = position; i < limit; i += 8) {
+ buffer.putFloat((float) inputBuffer.getDouble(i));
+ }
+ break;
case C.ENCODING_PCM_8BIT:
case C.ENCODING_PCM_16BIT:
case C.ENCODING_PCM_16BIT_BIG_ENDIAN:
diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/ExtractorUtil.java b/libraries/extractor/src/main/java/androidx/media3/extractor/ExtractorUtil.java
index 692adbc50d3..50e1becdff9 100644
--- a/libraries/extractor/src/main/java/androidx/media3/extractor/ExtractorUtil.java
+++ b/libraries/extractor/src/main/java/androidx/media3/extractor/ExtractorUtil.java
@@ -168,7 +168,9 @@ public static int getMaximumEncodedRateBytesPerSecond(@C.Encoding int encoding)
case C.ENCODING_PCM_32BIT_BIG_ENDIAN:
case C.ENCODING_PCM_8BIT:
case C.ENCODING_PCM_FLOAT:
+ case C.ENCODING_PCM_DOUBLE:
case C.ENCODING_AAC_ER_BSAC:
+ case C.ENCODING_DSD:
case C.ENCODING_INVALID:
case Format.NO_VALUE:
default: