mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Add support for Dolby Atmos
Issue: #2465 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=176341309
This commit is contained in:
parent
75acfc7957
commit
c4fe0e6482
@ -12,6 +12,8 @@
|
|||||||
* New Cast extension: Simplifies toggling between local and Cast playbacks.
|
* New Cast extension: Simplifies toggling between local and Cast playbacks.
|
||||||
* Support 32-bit PCM float output from `DefaultAudioSink`, and add an option to
|
* Support 32-bit PCM float output from `DefaultAudioSink`, and add an option to
|
||||||
use this with `FfmpegAudioRenderer`.
|
use this with `FfmpegAudioRenderer`.
|
||||||
|
* Support extraction and decoding of Dolby Atmos
|
||||||
|
([#2465](https://github.com/google/ExoPlayer/issues/2465)).
|
||||||
|
|
||||||
### 2.6.0 ###
|
### 2.6.0 ###
|
||||||
|
|
||||||
|
@ -15,6 +15,10 @@
|
|||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer2.audio;
|
package com.google.android.exoplayer2.audio;
|
||||||
|
|
||||||
|
import static com.google.android.exoplayer2.audio.Ac3Util.Ac3SyncFrameInfo.STREAM_TYPE_TYPE0;
|
||||||
|
import static com.google.android.exoplayer2.audio.Ac3Util.Ac3SyncFrameInfo.STREAM_TYPE_TYPE1;
|
||||||
|
import static com.google.android.exoplayer2.audio.Ac3Util.Ac3SyncFrameInfo.STREAM_TYPE_UNDEFINED;
|
||||||
|
|
||||||
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.drm.DrmInitData;
|
import com.google.android.exoplayer2.drm.DrmInitData;
|
||||||
@ -181,7 +185,14 @@ public final class Ac3Util {
|
|||||||
channelCount += 2;
|
channelCount += 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Format.createAudioSampleFormat(trackId, MimeTypes.AUDIO_E_AC3, null, Format.NO_VALUE,
|
String mimeType = MimeTypes.AUDIO_E_AC3;
|
||||||
|
if (data.bytesLeft() > 0) {
|
||||||
|
nextByte = data.readUnsignedByte();
|
||||||
|
if ((nextByte & 0x01) != 0) { // flag_ec3_extension_type_a
|
||||||
|
mimeType = MimeTypes.AUDIO_ATMOS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Format.createAudioSampleFormat(trackId, mimeType, null, Format.NO_VALUE,
|
||||||
Format.NO_VALUE, channelCount, sampleRate, null, drmInitData, 0, language);
|
Format.NO_VALUE, channelCount, sampleRate, null, drmInitData, 0, language);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -198,29 +209,176 @@ public final class Ac3Util {
|
|||||||
boolean isEac3 = data.readBits(5) == 16;
|
boolean isEac3 = data.readBits(5) == 16;
|
||||||
data.setPosition(initialPosition);
|
data.setPosition(initialPosition);
|
||||||
String mimeType;
|
String mimeType;
|
||||||
int streamType = Ac3SyncFrameInfo.STREAM_TYPE_UNDEFINED;
|
int streamType = STREAM_TYPE_UNDEFINED;
|
||||||
int sampleRate;
|
int sampleRate;
|
||||||
int acmod;
|
int acmod;
|
||||||
int frameSize;
|
int frameSize;
|
||||||
int sampleCount;
|
int sampleCount;
|
||||||
|
boolean lfeon;
|
||||||
|
int channelCount;
|
||||||
if (isEac3) {
|
if (isEac3) {
|
||||||
mimeType = MimeTypes.AUDIO_E_AC3;
|
// Syntax from ETSI TS 102 366 V1.2.1 subsections E.1.2.1 and E.1.2.2.
|
||||||
data.skipBits(16); // syncword
|
data.skipBits(16); // syncword
|
||||||
streamType = data.readBits(2);
|
streamType = data.readBits(2);
|
||||||
data.skipBits(3); // substreamid
|
data.skipBits(3); // substreamid
|
||||||
frameSize = (data.readBits(11) + 1) * 2;
|
frameSize = (data.readBits(11) + 1) * 2;
|
||||||
int fscod = data.readBits(2);
|
int fscod = data.readBits(2);
|
||||||
int audioBlocks;
|
int audioBlocks;
|
||||||
|
int numblkscod;
|
||||||
if (fscod == 3) {
|
if (fscod == 3) {
|
||||||
|
numblkscod = 3;
|
||||||
sampleRate = SAMPLE_RATE_BY_FSCOD2[data.readBits(2)];
|
sampleRate = SAMPLE_RATE_BY_FSCOD2[data.readBits(2)];
|
||||||
audioBlocks = 6;
|
audioBlocks = 6;
|
||||||
} else {
|
} else {
|
||||||
int numblkscod = data.readBits(2);
|
numblkscod = data.readBits(2);
|
||||||
audioBlocks = BLOCKS_PER_SYNCFRAME_BY_NUMBLKSCOD[numblkscod];
|
audioBlocks = BLOCKS_PER_SYNCFRAME_BY_NUMBLKSCOD[numblkscod];
|
||||||
sampleRate = SAMPLE_RATE_BY_FSCOD[fscod];
|
sampleRate = SAMPLE_RATE_BY_FSCOD[fscod];
|
||||||
}
|
}
|
||||||
sampleCount = AUDIO_SAMPLES_PER_AUDIO_BLOCK * audioBlocks;
|
sampleCount = AUDIO_SAMPLES_PER_AUDIO_BLOCK * audioBlocks;
|
||||||
acmod = data.readBits(3);
|
acmod = data.readBits(3);
|
||||||
|
lfeon = data.readBit();
|
||||||
|
channelCount = CHANNEL_COUNT_BY_ACMOD[acmod] + (lfeon ? 1 : 0);
|
||||||
|
data.skipBits(5 + 5); // bsid, dialnorm
|
||||||
|
if (data.readBit()) { // compre
|
||||||
|
data.skipBits(8); // compr
|
||||||
|
}
|
||||||
|
if (acmod == 0) {
|
||||||
|
data.skipBits(5); // dialnorm2
|
||||||
|
if (data.readBit()) { // compr2e
|
||||||
|
data.skipBits(8); // compr2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (streamType == STREAM_TYPE_TYPE1 && data.readBit()) { // chanmape
|
||||||
|
data.skipBits(16); // chanmap
|
||||||
|
}
|
||||||
|
if (data.readBit()) { // mixmdate
|
||||||
|
if (acmod > 2) {
|
||||||
|
data.skipBits(2); // dmixmod
|
||||||
|
}
|
||||||
|
if ((acmod & 0x01) != 0 && acmod > 2) {
|
||||||
|
data.skipBits(3 + 3); // ltrtcmixlev, lorocmixlev
|
||||||
|
}
|
||||||
|
if ((acmod & 0x04) != 0) {
|
||||||
|
data.skipBits(6); // ltrtsurmixlev, lorosurmixlev
|
||||||
|
}
|
||||||
|
if (lfeon && data.readBit()) { // lfemixlevcode
|
||||||
|
data.skipBits(5); // lfemixlevcod
|
||||||
|
}
|
||||||
|
if (streamType == STREAM_TYPE_TYPE0) {
|
||||||
|
if (data.readBit()) { // pgmscle
|
||||||
|
data.skipBits(6); //pgmscl
|
||||||
|
}
|
||||||
|
if (acmod == 0 && data.readBit()) { // pgmscl2e
|
||||||
|
data.skipBits(6); // pgmscl2
|
||||||
|
}
|
||||||
|
if (data.readBit()) { // extpgmscle
|
||||||
|
data.skipBits(6); // extpgmscl
|
||||||
|
}
|
||||||
|
int mixdef = data.readBits(2);
|
||||||
|
if (mixdef == 1) {
|
||||||
|
data.skipBits(1 + 1 + 3); // premixcmpsel, drcsrc, premixcmpscl
|
||||||
|
} else if (mixdef == 2) {
|
||||||
|
data.skipBits(12); // mixdata
|
||||||
|
} else if (mixdef == 3) {
|
||||||
|
int mixdeflen = data.readBits(5);
|
||||||
|
if (data.readBit()) { // mixdata2e
|
||||||
|
data.skipBits(1 + 1 + 3); // premixcmpsel, drcsrc, premixcmpscl
|
||||||
|
if (data.readBit()) { // extpgmlscle
|
||||||
|
data.skipBits(4); // extpgmlscl
|
||||||
|
}
|
||||||
|
if (data.readBit()) { // extpgmcscle
|
||||||
|
data.skipBits(4); // extpgmcscl
|
||||||
|
}
|
||||||
|
if (data.readBit()) { // extpgmrscle
|
||||||
|
data.skipBits(4); // extpgmrscl
|
||||||
|
}
|
||||||
|
if (data.readBit()) { // extpgmlsscle
|
||||||
|
data.skipBits(4); // extpgmlsscl
|
||||||
|
}
|
||||||
|
if (data.readBit()) { // extpgmrsscle
|
||||||
|
data.skipBits(4); // extpgmrsscl
|
||||||
|
}
|
||||||
|
if (data.readBit()) { // extpgmlfescle
|
||||||
|
data.skipBits(4); // extpgmlfescl
|
||||||
|
}
|
||||||
|
if (data.readBit()) { // dmixscle
|
||||||
|
data.skipBits(4); // dmixscl
|
||||||
|
}
|
||||||
|
if (data.readBit()) { // addche
|
||||||
|
if (data.readBit()) { // extpgmaux1scle
|
||||||
|
data.skipBits(4); // extpgmaux1scl
|
||||||
|
}
|
||||||
|
if (data.readBit()) { // extpgmaux2scle
|
||||||
|
data.skipBits(4); // extpgmaux2scl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (data.readBit()) { // mixdata3e
|
||||||
|
data.skipBits(5); // spchdat
|
||||||
|
if (data.readBit()) { // addspchdate
|
||||||
|
data.skipBits(5 + 2); // spchdat1, spchan1att
|
||||||
|
if (data.readBit()) { // addspdat1e
|
||||||
|
data.skipBits(5 + 3); // spchdat2, spchan2att
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data.skipBits(8 * (mixdeflen + 2)); // mixdata
|
||||||
|
data.byteAlign(); // mixdatafill
|
||||||
|
}
|
||||||
|
if (acmod < 2) {
|
||||||
|
if (data.readBit()) { // paninfoe
|
||||||
|
data.skipBits(8 + 6); // panmean, paninfo
|
||||||
|
}
|
||||||
|
if (acmod == 0) {
|
||||||
|
if (data.readBit()) { // paninfo2e
|
||||||
|
data.skipBits(8 + 6); // panmean2, paninfo2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (data.readBit()) { // frmmixcfginfoe
|
||||||
|
if (numblkscod == 0) {
|
||||||
|
data.skipBits(5); // blkmixcfginfo[0]
|
||||||
|
} else {
|
||||||
|
for (int blk = 0; blk < audioBlocks; blk++) {
|
||||||
|
if (data.readBit()) { // blkmixcfginfoe
|
||||||
|
data.skipBits(5); // blkmixcfginfo[blk]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (data.readBit()) { // infomdate
|
||||||
|
data.skipBits(3 + 1 + 1); // bsmod, copyrightb, origbs
|
||||||
|
if (acmod == 2) {
|
||||||
|
data.skipBits(2 + 2); // dsurmod, dheadphonmod
|
||||||
|
}
|
||||||
|
if (acmod >= 6) {
|
||||||
|
data.skipBits(2); // dsurexmod
|
||||||
|
}
|
||||||
|
if (data.readBit()) { // audioprodie
|
||||||
|
data.skipBits(5 + 2 + 1); // mixlevel, roomtyp, adconvtyp
|
||||||
|
}
|
||||||
|
if (acmod == 0 && data.readBit()) { // audioprodi2e
|
||||||
|
data.skipBits(5 + 2 + 1); // mixlevel2, roomtyp2, adconvtyp2
|
||||||
|
}
|
||||||
|
if (fscod < 3) {
|
||||||
|
data.skipBit(); // sourcefscod
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (streamType == 0 && numblkscod != 3) {
|
||||||
|
data.skipBit(); // convsync
|
||||||
|
}
|
||||||
|
if (streamType == 2 && (numblkscod == 3 || data.readBit())) { // blkid
|
||||||
|
data.skipBits(6); // frmsizecod
|
||||||
|
}
|
||||||
|
mimeType = MimeTypes.AUDIO_E_AC3;
|
||||||
|
if (data.readBit()) { // addbsie
|
||||||
|
int addbsil = data.readBits(6);
|
||||||
|
if (addbsil == 1 && data.readBits(8) == 1) { // addbsi
|
||||||
|
mimeType = MimeTypes.AUDIO_ATMOS;
|
||||||
|
}
|
||||||
|
}
|
||||||
} else /* is AC-3 */ {
|
} else /* is AC-3 */ {
|
||||||
mimeType = MimeTypes.AUDIO_AC3;
|
mimeType = MimeTypes.AUDIO_AC3;
|
||||||
data.skipBits(16 + 16); // syncword, crc1
|
data.skipBits(16 + 16); // syncword, crc1
|
||||||
@ -240,9 +398,9 @@ public final class Ac3Util {
|
|||||||
}
|
}
|
||||||
sampleRate = SAMPLE_RATE_BY_FSCOD[fscod];
|
sampleRate = SAMPLE_RATE_BY_FSCOD[fscod];
|
||||||
sampleCount = AC3_SYNCFRAME_AUDIO_SAMPLE_COUNT;
|
sampleCount = AC3_SYNCFRAME_AUDIO_SAMPLE_COUNT;
|
||||||
|
lfeon = data.readBit();
|
||||||
|
channelCount = CHANNEL_COUNT_BY_ACMOD[acmod] + (lfeon ? 1 : 0);
|
||||||
}
|
}
|
||||||
boolean lfeon = data.readBit();
|
|
||||||
int channelCount = CHANNEL_COUNT_BY_ACMOD[acmod] + (lfeon ? 1 : 0);
|
|
||||||
return new Ac3SyncFrameInfo(mimeType, streamType, channelCount, sampleRate, frameSize,
|
return new Ac3SyncFrameInfo(mimeType, streamType, channelCount, sampleRate, frameSize,
|
||||||
sampleCount);
|
sampleCount);
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,7 @@ public final class Ac3Reader implements ElementaryStreamReader {
|
|||||||
private static final int STATE_READING_HEADER = 1;
|
private static final int STATE_READING_HEADER = 1;
|
||||||
private static final int STATE_READING_SAMPLE = 2;
|
private static final int STATE_READING_SAMPLE = 2;
|
||||||
|
|
||||||
private static final int HEADER_SIZE = 8;
|
private static final int HEADER_SIZE = 128;
|
||||||
|
|
||||||
private final ParsableBitArray headerScratchBits;
|
private final ParsableBitArray headerScratchBits;
|
||||||
private final ParsableByteArray headerScratchBytes;
|
private final ParsableByteArray headerScratchBytes;
|
||||||
|
@ -20,6 +20,7 @@ import android.annotation.TargetApi;
|
|||||||
import android.media.MediaCodecInfo.CodecCapabilities;
|
import android.media.MediaCodecInfo.CodecCapabilities;
|
||||||
import android.media.MediaCodecInfo.CodecProfileLevel;
|
import android.media.MediaCodecInfo.CodecProfileLevel;
|
||||||
import android.media.MediaCodecList;
|
import android.media.MediaCodecList;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
@ -120,7 +121,7 @@ public final class MediaCodecUtil {
|
|||||||
* exists.
|
* exists.
|
||||||
* @throws DecoderQueryException If there was an error querying the available decoders.
|
* @throws DecoderQueryException If there was an error querying the available decoders.
|
||||||
*/
|
*/
|
||||||
public static MediaCodecInfo getDecoderInfo(String mimeType, boolean secure)
|
public static @Nullable MediaCodecInfo getDecoderInfo(String mimeType, boolean secure)
|
||||||
throws DecoderQueryException {
|
throws DecoderQueryException {
|
||||||
List<MediaCodecInfo> decoderInfos = getDecoderInfos(mimeType, secure);
|
List<MediaCodecInfo> decoderInfos = getDecoderInfos(mimeType, secure);
|
||||||
return decoderInfos.isEmpty() ? null : decoderInfos.get(0);
|
return decoderInfos.isEmpty() ? null : decoderInfos.get(0);
|
||||||
@ -140,27 +141,34 @@ public final class MediaCodecUtil {
|
|||||||
public static synchronized List<MediaCodecInfo> getDecoderInfos(String mimeType,
|
public static synchronized List<MediaCodecInfo> getDecoderInfos(String mimeType,
|
||||||
boolean secure) throws DecoderQueryException {
|
boolean secure) throws DecoderQueryException {
|
||||||
CodecKey key = new CodecKey(mimeType, secure);
|
CodecKey key = new CodecKey(mimeType, secure);
|
||||||
List<MediaCodecInfo> decoderInfos = decoderInfosCache.get(key);
|
List<MediaCodecInfo> cachedDecoderInfos = decoderInfosCache.get(key);
|
||||||
if (decoderInfos != null) {
|
if (cachedDecoderInfos != null) {
|
||||||
return decoderInfos;
|
return cachedDecoderInfos;
|
||||||
}
|
}
|
||||||
MediaCodecListCompat mediaCodecList = Util.SDK_INT >= 21
|
MediaCodecListCompat mediaCodecList = Util.SDK_INT >= 21
|
||||||
? new MediaCodecListCompatV21(secure) : new MediaCodecListCompatV16();
|
? new MediaCodecListCompatV21(secure) : new MediaCodecListCompatV16();
|
||||||
decoderInfos = getDecoderInfosInternal(key, mediaCodecList);
|
ArrayList<MediaCodecInfo> decoderInfos = getDecoderInfosInternal(key, mediaCodecList, mimeType);
|
||||||
if (secure && decoderInfos.isEmpty() && 21 <= Util.SDK_INT && Util.SDK_INT <= 23) {
|
if (secure && decoderInfos.isEmpty() && 21 <= Util.SDK_INT && Util.SDK_INT <= 23) {
|
||||||
// Some devices don't list secure decoders on API level 21 [Internal: b/18678462]. Try the
|
// Some devices don't list secure decoders on API level 21 [Internal: b/18678462]. Try the
|
||||||
// legacy path. We also try this path on API levels 22 and 23 as a defensive measure.
|
// legacy path. We also try this path on API levels 22 and 23 as a defensive measure.
|
||||||
mediaCodecList = new MediaCodecListCompatV16();
|
mediaCodecList = new MediaCodecListCompatV16();
|
||||||
decoderInfos = getDecoderInfosInternal(key, mediaCodecList);
|
decoderInfos = getDecoderInfosInternal(key, mediaCodecList, mimeType);
|
||||||
if (!decoderInfos.isEmpty()) {
|
if (!decoderInfos.isEmpty()) {
|
||||||
Log.w(TAG, "MediaCodecList API didn't list secure decoder for: " + mimeType
|
Log.w(TAG, "MediaCodecList API didn't list secure decoder for: " + mimeType
|
||||||
+ ". Assuming: " + decoderInfos.get(0).name);
|
+ ". Assuming: " + decoderInfos.get(0).name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (MimeTypes.AUDIO_ATMOS.equals(mimeType)) {
|
||||||
|
// E-AC3 decoders can decode Atmos streams, but in 2-D rather than 3-D.
|
||||||
|
CodecKey eac3Key = new CodecKey(MimeTypes.AUDIO_E_AC3, key.secure);
|
||||||
|
ArrayList<MediaCodecInfo> eac3DecoderInfos =
|
||||||
|
getDecoderInfosInternal(eac3Key, mediaCodecList, mimeType);
|
||||||
|
decoderInfos.addAll(eac3DecoderInfos);
|
||||||
|
}
|
||||||
applyWorkarounds(decoderInfos);
|
applyWorkarounds(decoderInfos);
|
||||||
decoderInfos = Collections.unmodifiableList(decoderInfos);
|
List<MediaCodecInfo> unmodifiableDecoderInfos = Collections.unmodifiableList(decoderInfos);
|
||||||
decoderInfosCache.put(key, decoderInfos);
|
decoderInfosCache.put(key, unmodifiableDecoderInfos);
|
||||||
return decoderInfos;
|
return unmodifiableDecoderInfos;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -212,10 +220,21 @@ public final class MediaCodecUtil {
|
|||||||
|
|
||||||
// Internal methods.
|
// Internal methods.
|
||||||
|
|
||||||
private static List<MediaCodecInfo> getDecoderInfosInternal(
|
/**
|
||||||
CodecKey key, MediaCodecListCompat mediaCodecList) throws DecoderQueryException {
|
* Returns {@link MediaCodecInfo}s for the given codec {@code key} in the order given by
|
||||||
|
* {@code mediaCodecList}.
|
||||||
|
*
|
||||||
|
* @param key The codec key.
|
||||||
|
* @param mediaCodecList The codec list.
|
||||||
|
* @param requestedMimeType The originally requested MIME type, which may differ from the codec
|
||||||
|
* key MIME type if the codec key is being considered as a fallback.
|
||||||
|
* @return The codec information for usable codecs matching the specified key.
|
||||||
|
* @throws DecoderQueryException If there was an error querying the available decoders.
|
||||||
|
*/
|
||||||
|
private static ArrayList<MediaCodecInfo> getDecoderInfosInternal(CodecKey key,
|
||||||
|
MediaCodecListCompat mediaCodecList, String requestedMimeType) throws DecoderQueryException {
|
||||||
try {
|
try {
|
||||||
List<MediaCodecInfo> decoderInfos = new ArrayList<>();
|
ArrayList<MediaCodecInfo> decoderInfos = new ArrayList<>();
|
||||||
String mimeType = key.mimeType;
|
String mimeType = key.mimeType;
|
||||||
int numberOfCodecs = mediaCodecList.getCodecCount();
|
int numberOfCodecs = mediaCodecList.getCodecCount();
|
||||||
boolean secureDecodersExplicit = mediaCodecList.secureDecodersExplicit();
|
boolean secureDecodersExplicit = mediaCodecList.secureDecodersExplicit();
|
||||||
@ -223,7 +242,7 @@ public final class MediaCodecUtil {
|
|||||||
for (int i = 0; i < numberOfCodecs; i++) {
|
for (int i = 0; i < numberOfCodecs; i++) {
|
||||||
android.media.MediaCodecInfo codecInfo = mediaCodecList.getCodecInfoAt(i);
|
android.media.MediaCodecInfo codecInfo = mediaCodecList.getCodecInfoAt(i);
|
||||||
String codecName = codecInfo.getName();
|
String codecName = codecInfo.getName();
|
||||||
if (isCodecUsableDecoder(codecInfo, codecName, secureDecodersExplicit)) {
|
if (isCodecUsableDecoder(codecInfo, codecName, secureDecodersExplicit, requestedMimeType)) {
|
||||||
for (String supportedType : codecInfo.getSupportedTypes()) {
|
for (String supportedType : codecInfo.getSupportedTypes()) {
|
||||||
if (supportedType.equalsIgnoreCase(mimeType)) {
|
if (supportedType.equalsIgnoreCase(mimeType)) {
|
||||||
try {
|
try {
|
||||||
@ -265,9 +284,16 @@ public final class MediaCodecUtil {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether the specified codec is usable for decoding on the current device.
|
* Returns whether the specified codec is usable for decoding on the current device.
|
||||||
|
*
|
||||||
|
* @param info The codec information.
|
||||||
|
* @param name The name of the codec
|
||||||
|
* @param secureDecodersExplicit Whether secure decoders were explicitly listed, if present.
|
||||||
|
* @param requestedMimeType The originally requested MIME type, which may differ from the codec
|
||||||
|
* key MIME type if the codec key is being considered as a fallback.
|
||||||
|
* @return Whether the specified codec is usable for decoding on the current device.
|
||||||
*/
|
*/
|
||||||
private static boolean isCodecUsableDecoder(android.media.MediaCodecInfo info, String name,
|
private static boolean isCodecUsableDecoder(android.media.MediaCodecInfo info, String name,
|
||||||
boolean secureDecodersExplicit) {
|
boolean secureDecodersExplicit, String requestedMimeType) {
|
||||||
if (info.isEncoder() || (!secureDecodersExplicit && name.endsWith(".secure"))) {
|
if (info.isEncoder() || (!secureDecodersExplicit && name.endsWith(".secure"))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -356,6 +382,12 @@ public final class MediaCodecUtil {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MTK E-AC3 decoder doesn't support decoding Atmos streams in 2-D. See [Internal: b/69400041].
|
||||||
|
if (MimeTypes.AUDIO_ATMOS.equals(requestedMimeType)
|
||||||
|
&& "OMX.MTK.AUDIO.DECODER.DSPAC3".equals(name)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,6 +51,7 @@ public final class MimeTypes {
|
|||||||
public static final String AUDIO_MLAW = BASE_TYPE_AUDIO + "/g711-mlaw";
|
public static final String AUDIO_MLAW = BASE_TYPE_AUDIO + "/g711-mlaw";
|
||||||
public static final String AUDIO_AC3 = BASE_TYPE_AUDIO + "/ac3";
|
public static final String AUDIO_AC3 = BASE_TYPE_AUDIO + "/ac3";
|
||||||
public static final String AUDIO_E_AC3 = BASE_TYPE_AUDIO + "/eac3";
|
public static final String AUDIO_E_AC3 = BASE_TYPE_AUDIO + "/eac3";
|
||||||
|
public static final String AUDIO_ATMOS = BASE_TYPE_AUDIO + "/eac3-joc";
|
||||||
public static final String AUDIO_TRUEHD = BASE_TYPE_AUDIO + "/true-hd";
|
public static final String AUDIO_TRUEHD = BASE_TYPE_AUDIO + "/true-hd";
|
||||||
public static final String AUDIO_DTS = BASE_TYPE_AUDIO + "/vnd.dts";
|
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_HD = BASE_TYPE_AUDIO + "/vnd.dts.hd";
|
||||||
@ -195,6 +196,8 @@ public final class MimeTypes {
|
|||||||
return MimeTypes.AUDIO_AC3;
|
return MimeTypes.AUDIO_AC3;
|
||||||
} else if (codec.startsWith("ec-3") || codec.startsWith("dec3")) {
|
} else if (codec.startsWith("ec-3") || codec.startsWith("dec3")) {
|
||||||
return MimeTypes.AUDIO_E_AC3;
|
return MimeTypes.AUDIO_E_AC3;
|
||||||
|
} else if (codec.startsWith("ec+3")) {
|
||||||
|
return MimeTypes.AUDIO_ATMOS;
|
||||||
} else if (codec.startsWith("dtsc") || codec.startsWith("dtse")) {
|
} else if (codec.startsWith("dtsc") || codec.startsWith("dtse")) {
|
||||||
return MimeTypes.AUDIO_DTS;
|
return MimeTypes.AUDIO_DTS;
|
||||||
} else if (codec.startsWith("dtsh") || codec.startsWith("dtsl")) {
|
} else if (codec.startsWith("dtsh") || codec.startsWith("dtsl")) {
|
||||||
@ -252,6 +255,7 @@ public final class MimeTypes {
|
|||||||
case MimeTypes.AUDIO_AC3:
|
case MimeTypes.AUDIO_AC3:
|
||||||
return C.ENCODING_AC3;
|
return C.ENCODING_AC3;
|
||||||
case MimeTypes.AUDIO_E_AC3:
|
case MimeTypes.AUDIO_E_AC3:
|
||||||
|
case MimeTypes.AUDIO_ATMOS:
|
||||||
return C.ENCODING_E_AC3;
|
return C.ENCODING_E_AC3;
|
||||||
case MimeTypes.AUDIO_DTS:
|
case MimeTypes.AUDIO_DTS:
|
||||||
return C.ENCODING_DTS;
|
return C.ENCODING_DTS;
|
||||||
|
@ -460,6 +460,7 @@ public class DashManifestParser extends DefaultHandler
|
|||||||
String drmSchemeType = null;
|
String drmSchemeType = null;
|
||||||
ArrayList<SchemeData> drmSchemeDatas = new ArrayList<>();
|
ArrayList<SchemeData> drmSchemeDatas = new ArrayList<>();
|
||||||
ArrayList<Descriptor> inbandEventStreams = new ArrayList<>();
|
ArrayList<Descriptor> inbandEventStreams = new ArrayList<>();
|
||||||
|
ArrayList<Descriptor> supplementalProperties = new ArrayList<>();
|
||||||
|
|
||||||
boolean seenFirstBaseUrl = false;
|
boolean seenFirstBaseUrl = false;
|
||||||
do {
|
do {
|
||||||
@ -487,12 +488,14 @@ public class DashManifestParser extends DefaultHandler
|
|||||||
}
|
}
|
||||||
} else if (XmlPullParserUtil.isStartTag(xpp, "InbandEventStream")) {
|
} else if (XmlPullParserUtil.isStartTag(xpp, "InbandEventStream")) {
|
||||||
inbandEventStreams.add(parseDescriptor(xpp, "InbandEventStream"));
|
inbandEventStreams.add(parseDescriptor(xpp, "InbandEventStream"));
|
||||||
|
} else if (XmlPullParserUtil.isStartTag(xpp, "SupplementalProperty")) {
|
||||||
|
supplementalProperties.add(parseDescriptor(xpp, "SupplementalProperty"));
|
||||||
}
|
}
|
||||||
} while (!XmlPullParserUtil.isEndTag(xpp, "Representation"));
|
} while (!XmlPullParserUtil.isEndTag(xpp, "Representation"));
|
||||||
|
|
||||||
Format format = buildFormat(id, mimeType, width, height, frameRate, audioChannels,
|
Format format = buildFormat(id, mimeType, width, height, frameRate, audioChannels,
|
||||||
audioSamplingRate, bandwidth, adaptationSetLanguage, adaptationSetSelectionFlags,
|
audioSamplingRate, bandwidth, adaptationSetLanguage, adaptationSetSelectionFlags,
|
||||||
adaptationSetAccessibilityDescriptors, codecs);
|
adaptationSetAccessibilityDescriptors, codecs, supplementalProperties);
|
||||||
segmentBase = segmentBase != null ? segmentBase : new SingleSegmentBase();
|
segmentBase = segmentBase != null ? segmentBase : new SingleSegmentBase();
|
||||||
|
|
||||||
return new RepresentationInfo(format, baseUrl, segmentBase, drmSchemeType, drmSchemeDatas,
|
return new RepresentationInfo(format, baseUrl, segmentBase, drmSchemeType, drmSchemeDatas,
|
||||||
@ -502,9 +505,12 @@ public class DashManifestParser extends DefaultHandler
|
|||||||
protected Format buildFormat(String id, String containerMimeType, int width, int height,
|
protected Format buildFormat(String id, String containerMimeType, int width, int height,
|
||||||
float frameRate, int audioChannels, int audioSamplingRate, int bitrate, String language,
|
float frameRate, int audioChannels, int audioSamplingRate, int bitrate, String language,
|
||||||
@C.SelectionFlags int selectionFlags, List<Descriptor> accessibilityDescriptors,
|
@C.SelectionFlags int selectionFlags, List<Descriptor> accessibilityDescriptors,
|
||||||
String codecs) {
|
String codecs, List<Descriptor> supplementalProperties) {
|
||||||
String sampleMimeType = getSampleMimeType(containerMimeType, codecs);
|
String sampleMimeType = getSampleMimeType(containerMimeType, codecs);
|
||||||
if (sampleMimeType != null) {
|
if (sampleMimeType != null) {
|
||||||
|
if (MimeTypes.AUDIO_E_AC3.equals(sampleMimeType)) {
|
||||||
|
sampleMimeType = parseEac3SupplementalProperties(supplementalProperties);
|
||||||
|
}
|
||||||
if (MimeTypes.isVideo(sampleMimeType)) {
|
if (MimeTypes.isVideo(sampleMimeType)) {
|
||||||
return Format.createVideoContainerFormat(id, containerMimeType, sampleMimeType, codecs,
|
return Format.createVideoContainerFormat(id, containerMimeType, sampleMimeType, codecs,
|
||||||
bitrate, width, height, frameRate, null, selectionFlags);
|
bitrate, width, height, frameRate, null, selectionFlags);
|
||||||
@ -1045,6 +1051,18 @@ public class DashManifestParser extends DefaultHandler
|
|||||||
return Format.NO_VALUE;
|
return Format.NO_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected static String parseEac3SupplementalProperties(List<Descriptor> supplementalProperties) {
|
||||||
|
for (int i = 0; i < supplementalProperties.size(); i++) {
|
||||||
|
Descriptor descriptor = supplementalProperties.get(i);
|
||||||
|
String schemeIdUri = descriptor.schemeIdUri;
|
||||||
|
if ("tag:dolby.com,2014:dash:DolbyDigitalPlusExtensionType:2014".equals(schemeIdUri)
|
||||||
|
&& "ec+3".equals(descriptor.value)) {
|
||||||
|
return MimeTypes.AUDIO_ATMOS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return MimeTypes.AUDIO_E_AC3;
|
||||||
|
}
|
||||||
|
|
||||||
protected static float parseFrameRate(XmlPullParser xpp, float defaultValue) {
|
protected static float parseFrameRate(XmlPullParser xpp, float defaultValue) {
|
||||||
float frameRate = defaultValue;
|
float frameRate = defaultValue;
|
||||||
String frameRateAttribute = xpp.getAttributeValue(null, "frameRate");
|
String frameRateAttribute = xpp.getAttributeValue(null, "frameRate");
|
||||||
|
Loading…
x
Reference in New Issue
Block a user