From aedf538aa87c6a7646e2f1a33aa07f196f06a538 Mon Sep 17 00:00:00 2001 From: William King Date: Thu, 23 Jul 2020 17:57:33 -0700 Subject: [PATCH] MKV Dolby Vision Support --- .../extractor/mkv/MatroskaExtractor.java | 66 +++++++++++++++++-- 1 file changed, 62 insertions(+), 4 deletions(-) diff --git a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/mkv/MatroskaExtractor.java b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/mkv/MatroskaExtractor.java index f566493ada..7fcb5c4f22 100644 --- a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/mkv/MatroskaExtractor.java +++ b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/mkv/MatroskaExtractor.java @@ -45,6 +45,7 @@ import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.video.AvcConfig; import com.google.android.exoplayer2.video.ColorInfo; +import com.google.android.exoplayer2.video.DolbyVisionConfig; import com.google.android.exoplayer2.video.HevcConfig; import java.io.IOException; import java.lang.annotation.Documented; @@ -155,8 +156,13 @@ public class MatroskaExtractor implements Extractor { private static final int ID_BLOCK = 0xA1; private static final int ID_BLOCK_DURATION = 0x9B; private static final int ID_BLOCK_ADDITIONS = 0x75A1; + private static final int ID_BLOCK_ADDITION_MAPPING = 0x41E4; private static final int ID_BLOCK_MORE = 0xA6; private static final int ID_BLOCK_ADD_ID = 0xEE; + private static final int ID_BLOCK_ADD_ID_VALUE = 0x41F0; + private static final int ID_BLOCK_ADD_ID_NAME = 0x41A4; + private static final int ID_BLOCK_ADD_ID_TYPE = 0x41E7; + private static final int ID_BLOCK_ADD_ID_EXTRA_DATA = 0x41ED; private static final int ID_BLOCK_ADDITIONAL = 0xA5; private static final int ID_REFERENCE_BLOCK = 0xFB; private static final int ID_TRACKS = 0x1654AE6B; @@ -231,6 +237,18 @@ public class MatroskaExtractor implements Extractor { */ private static final int BLOCK_ADDITIONAL_ID_VP9_ITU_T_35 = 4; + /** + * Dolby Vision configuration for profiles <= 7 + * https://www.matroska.org/technical/codec_specs.html + */ + private static final int BLOCK_ADDITIONAL_ID_DVCC = 0x64766343; + + /** + * Dolby Vision configuration for profiles > 7 + * https://www.matroska.org/technical/codec_specs.html + */ + private static final int BLOCK_ADDITIONAL_ID_DVVC = 0x64767643; + private static final int LACING_NONE = 0; private static final int LACING_XIPH = 1; private static final int LACING_FIXED_SIZE = 2; @@ -253,8 +271,8 @@ public class MatroskaExtractor implements Extractor { */ private static final byte[] SUBRIP_PREFIX = new byte[] { - 49, 10, 48, 48, 58, 48, 48, 58, 48, 48, 44, 48, 48, 48, 32, 45, 45, 62, 32, 48, 48, 58, 48, - 48, 58, 48, 48, 44, 48, 48, 48, 10 + 49, 10, 48, 48, 58, 48, 48, 58, 48, 48, 44, 48, 48, 48, 32, 45, 45, 62, 32, 48, 48, 58, 48, + 48, 58, 48, 48, 44, 48, 48, 48, 10 }; /** * The byte offset of the end timecode in {@link #SUBRIP_PREFIX}. @@ -288,8 +306,8 @@ public class MatroskaExtractor implements Extractor { */ private static final byte[] SSA_PREFIX = new byte[] { - 68, 105, 97, 108, 111, 103, 117, 101, 58, 32, 48, 58, 48, 48, 58, 48, 48, 58, 48, 48, 44, - 48, 58, 48, 48, 58, 48, 48, 58, 48, 48, 44 + 68, 105, 97, 108, 111, 103, 117, 101, 58, 32, 48, 58, 48, 48, 58, 48, 48, 58, 48, 48, 44, + 48, 58, 48, 48, 58, 48, 48, 58, 48, 48, 44 }; /** * The byte offset of the end timecode in {@link #SSA_PREFIX}. @@ -510,6 +528,7 @@ public class MatroskaExtractor implements Extractor { case ID_CUE_TRACK_POSITIONS: case ID_BLOCK_GROUP: case ID_BLOCK_ADDITIONS: + case ID_BLOCK_ADDITION_MAPPING: case ID_BLOCK_MORE: case ID_PROJECTION: case ID_COLOUR: @@ -552,11 +571,14 @@ public class MatroskaExtractor implements Extractor { case ID_MAX_FALL: case ID_PROJECTION_TYPE: case ID_BLOCK_ADD_ID: + case ID_BLOCK_ADD_ID_VALUE: + case ID_BLOCK_ADD_ID_TYPE: return EbmlProcessor.ELEMENT_TYPE_UNSIGNED_INT; case ID_DOC_TYPE: case ID_NAME: case ID_CODEC_ID: case ID_LANGUAGE: + case ID_BLOCK_ADD_ID_NAME: return EbmlProcessor.ELEMENT_TYPE_STRING; case ID_SEEK_ID: case ID_CONTENT_COMPRESSION_SETTINGS: @@ -566,6 +588,7 @@ public class MatroskaExtractor implements Extractor { case ID_CODEC_PRIVATE: case ID_PROJECTION_PRIVATE: case ID_BLOCK_ADDITIONAL: + case ID_BLOCK_ADD_ID_EXTRA_DATA: return EbmlProcessor.ELEMENT_TYPE_BINARY; case ID_DURATION: case ID_SAMPLING_FREQUENCY: @@ -968,6 +991,9 @@ public class MatroskaExtractor implements Extractor { case ID_BLOCK_ADD_ID: blockAdditionalId = (int) value; break; + case ID_BLOCK_ADD_ID_TYPE: + currentTrack.blockAdditionalId = (int) value; + break; default: break; } @@ -1092,6 +1118,9 @@ public class MatroskaExtractor implements Extractor { currentTrack.cryptoData = new TrackOutput.CryptoData(C.CRYPTO_MODE_AES_CTR, encryptionKey, 0, 0); // We assume patternless AES-CTR. break; + case ID_BLOCK_ADD_ID_EXTRA_DATA: + handleBlockAddIDExtraData(currentTrack, input, contentSize); + break; case ID_SIMPLE_BLOCK: case ID_BLOCK: // Please refer to http://www.matroska.org/technical/specs/index.html#simpleblock_structure @@ -1243,6 +1272,7 @@ public class MatroskaExtractor implements Extractor { protected void handleBlockAdditionalData( Track track, int blockAdditionalId, ExtractorInput input, int contentSize) throws IOException { + if (blockAdditionalId == BLOCK_ADDITIONAL_ID_VP9_ITU_T_35 && CODEC_ID_VP9.equals(track.codecId)) { blockAdditionalData.reset(contentSize); @@ -1253,6 +1283,19 @@ public class MatroskaExtractor implements Extractor { } } + protected void handleBlockAddIDExtraData( + Track track, ExtractorInput input, int contentSize) + throws IOException { + + if (track.blockAdditionalId == BLOCK_ADDITIONAL_ID_DVVC || track.blockAdditionalId == BLOCK_ADDITIONAL_ID_DVCC) { + track.doviDecoderConfigurationRecord = new byte[contentSize]; + input.readFully(track.doviDecoderConfigurationRecord, 0, contentSize); + } else { + // Unhandled block additional data. + input.skipFully(contentSize); + } + } + private void commitSampleToOutput( Track track, long timeUs, @C.BufferFlags int flags, int size, int offset) { if (track.trueHdSampleRechunker != null) { @@ -1915,6 +1958,8 @@ public class MatroskaExtractor implements Extractor { public float whitePointChromaticityY = Format.NO_VALUE; public float maxMasteringLuminance = Format.NO_VALUE; public float minMasteringLuminance = Format.NO_VALUE; + // DOVIDecoderConfigurationRecord structure as defined in [@!DolbyVisionWithinIso.2020-02] + @Nullable public byte[] doviDecoderConfigurationRecord = null; // Audio elements. Initially set to their default values. public int channelCount = 1; @@ -1933,6 +1978,9 @@ public class MatroskaExtractor implements Extractor { public TrackOutput output; public int nalUnitLengthFieldLength; + // Block additional state + private int blockAdditionalId; + /** Initializes the track with an output. */ public void initializeOutput(ExtractorOutput output, int trackId) throws ParserException { String mimeType; @@ -2085,6 +2133,16 @@ public class MatroskaExtractor implements Extractor { throw new ParserException("Unrecognized codec identifier."); } + + if(doviDecoderConfigurationRecord != null) { + @Nullable DolbyVisionConfig dolbyVisionConfig = DolbyVisionConfig + .parse(new ParsableByteArray(doviDecoderConfigurationRecord)); + if (dolbyVisionConfig != null) { + codecs = dolbyVisionConfig.codecs; + mimeType = MimeTypes.VIDEO_DOLBY_VISION; + } + } + @C.SelectionFlags int selectionFlags = 0; selectionFlags |= flagDefault ? C.SELECTION_FLAG_DEFAULT : 0; selectionFlags |= flagForced ? C.SELECTION_FLAG_FORCED : 0;