MKV Dolby Vision Support

This commit is contained in:
William King 2020-07-23 17:57:33 -07:00
parent f205539616
commit aedf538aa8

View File

@ -45,6 +45,7 @@ import com.google.android.exoplayer2.util.ParsableByteArray;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import com.google.android.exoplayer2.video.AvcConfig; import com.google.android.exoplayer2.video.AvcConfig;
import com.google.android.exoplayer2.video.ColorInfo; import com.google.android.exoplayer2.video.ColorInfo;
import com.google.android.exoplayer2.video.DolbyVisionConfig;
import com.google.android.exoplayer2.video.HevcConfig; import com.google.android.exoplayer2.video.HevcConfig;
import java.io.IOException; import java.io.IOException;
import java.lang.annotation.Documented; 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 = 0xA1;
private static final int ID_BLOCK_DURATION = 0x9B; private static final int ID_BLOCK_DURATION = 0x9B;
private static final int ID_BLOCK_ADDITIONS = 0x75A1; 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_MORE = 0xA6;
private static final int ID_BLOCK_ADD_ID = 0xEE; 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_BLOCK_ADDITIONAL = 0xA5;
private static final int ID_REFERENCE_BLOCK = 0xFB; private static final int ID_REFERENCE_BLOCK = 0xFB;
private static final int ID_TRACKS = 0x1654AE6B; 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; 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_NONE = 0;
private static final int LACING_XIPH = 1; private static final int LACING_XIPH = 1;
private static final int LACING_FIXED_SIZE = 2; private static final int LACING_FIXED_SIZE = 2;
@ -253,8 +271,8 @@ public class MatroskaExtractor implements Extractor {
*/ */
private static final byte[] SUBRIP_PREFIX = private static final byte[] SUBRIP_PREFIX =
new byte[] { 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, 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 48, 58, 48, 48, 44, 48, 48, 48, 10
}; };
/** /**
* The byte offset of the end timecode in {@link #SUBRIP_PREFIX}. * 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 = private static final byte[] SSA_PREFIX =
new byte[] { new byte[] {
68, 105, 97, 108, 111, 103, 117, 101, 58, 32, 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 48, 58, 48, 48, 58, 48, 48, 58, 48, 48, 44
}; };
/** /**
* The byte offset of the end timecode in {@link #SSA_PREFIX}. * 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_CUE_TRACK_POSITIONS:
case ID_BLOCK_GROUP: case ID_BLOCK_GROUP:
case ID_BLOCK_ADDITIONS: case ID_BLOCK_ADDITIONS:
case ID_BLOCK_ADDITION_MAPPING:
case ID_BLOCK_MORE: case ID_BLOCK_MORE:
case ID_PROJECTION: case ID_PROJECTION:
case ID_COLOUR: case ID_COLOUR:
@ -552,11 +571,14 @@ public class MatroskaExtractor implements Extractor {
case ID_MAX_FALL: case ID_MAX_FALL:
case ID_PROJECTION_TYPE: case ID_PROJECTION_TYPE:
case ID_BLOCK_ADD_ID: case ID_BLOCK_ADD_ID:
case ID_BLOCK_ADD_ID_VALUE:
case ID_BLOCK_ADD_ID_TYPE:
return EbmlProcessor.ELEMENT_TYPE_UNSIGNED_INT; return EbmlProcessor.ELEMENT_TYPE_UNSIGNED_INT;
case ID_DOC_TYPE: case ID_DOC_TYPE:
case ID_NAME: case ID_NAME:
case ID_CODEC_ID: case ID_CODEC_ID:
case ID_LANGUAGE: case ID_LANGUAGE:
case ID_BLOCK_ADD_ID_NAME:
return EbmlProcessor.ELEMENT_TYPE_STRING; return EbmlProcessor.ELEMENT_TYPE_STRING;
case ID_SEEK_ID: case ID_SEEK_ID:
case ID_CONTENT_COMPRESSION_SETTINGS: case ID_CONTENT_COMPRESSION_SETTINGS:
@ -566,6 +588,7 @@ public class MatroskaExtractor implements Extractor {
case ID_CODEC_PRIVATE: case ID_CODEC_PRIVATE:
case ID_PROJECTION_PRIVATE: case ID_PROJECTION_PRIVATE:
case ID_BLOCK_ADDITIONAL: case ID_BLOCK_ADDITIONAL:
case ID_BLOCK_ADD_ID_EXTRA_DATA:
return EbmlProcessor.ELEMENT_TYPE_BINARY; return EbmlProcessor.ELEMENT_TYPE_BINARY;
case ID_DURATION: case ID_DURATION:
case ID_SAMPLING_FREQUENCY: case ID_SAMPLING_FREQUENCY:
@ -968,6 +991,9 @@ public class MatroskaExtractor implements Extractor {
case ID_BLOCK_ADD_ID: case ID_BLOCK_ADD_ID:
blockAdditionalId = (int) value; blockAdditionalId = (int) value;
break; break;
case ID_BLOCK_ADD_ID_TYPE:
currentTrack.blockAdditionalId = (int) value;
break;
default: default:
break; break;
} }
@ -1092,6 +1118,9 @@ public class MatroskaExtractor implements Extractor {
currentTrack.cryptoData = new TrackOutput.CryptoData(C.CRYPTO_MODE_AES_CTR, encryptionKey, currentTrack.cryptoData = new TrackOutput.CryptoData(C.CRYPTO_MODE_AES_CTR, encryptionKey,
0, 0); // We assume patternless AES-CTR. 0, 0); // We assume patternless AES-CTR.
break; break;
case ID_BLOCK_ADD_ID_EXTRA_DATA:
handleBlockAddIDExtraData(currentTrack, input, contentSize);
break;
case ID_SIMPLE_BLOCK: case ID_SIMPLE_BLOCK:
case ID_BLOCK: case ID_BLOCK:
// Please refer to http://www.matroska.org/technical/specs/index.html#simpleblock_structure // 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( protected void handleBlockAdditionalData(
Track track, int blockAdditionalId, ExtractorInput input, int contentSize) Track track, int blockAdditionalId, ExtractorInput input, int contentSize)
throws IOException { throws IOException {
if (blockAdditionalId == BLOCK_ADDITIONAL_ID_VP9_ITU_T_35 if (blockAdditionalId == BLOCK_ADDITIONAL_ID_VP9_ITU_T_35
&& CODEC_ID_VP9.equals(track.codecId)) { && CODEC_ID_VP9.equals(track.codecId)) {
blockAdditionalData.reset(contentSize); 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( private void commitSampleToOutput(
Track track, long timeUs, @C.BufferFlags int flags, int size, int offset) { Track track, long timeUs, @C.BufferFlags int flags, int size, int offset) {
if (track.trueHdSampleRechunker != null) { if (track.trueHdSampleRechunker != null) {
@ -1915,6 +1958,8 @@ public class MatroskaExtractor implements Extractor {
public float whitePointChromaticityY = Format.NO_VALUE; public float whitePointChromaticityY = Format.NO_VALUE;
public float maxMasteringLuminance = Format.NO_VALUE; public float maxMasteringLuminance = Format.NO_VALUE;
public float minMasteringLuminance = 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. // Audio elements. Initially set to their default values.
public int channelCount = 1; public int channelCount = 1;
@ -1933,6 +1978,9 @@ public class MatroskaExtractor implements Extractor {
public TrackOutput output; public TrackOutput output;
public int nalUnitLengthFieldLength; public int nalUnitLengthFieldLength;
// Block additional state
private int blockAdditionalId;
/** Initializes the track with an output. */ /** Initializes the track with an output. */
public void initializeOutput(ExtractorOutput output, int trackId) throws ParserException { public void initializeOutput(ExtractorOutput output, int trackId) throws ParserException {
String mimeType; String mimeType;
@ -2085,6 +2133,16 @@ public class MatroskaExtractor implements Extractor {
throw new ParserException("Unrecognized codec identifier."); 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; @C.SelectionFlags int selectionFlags = 0;
selectionFlags |= flagDefault ? C.SELECTION_FLAG_DEFAULT : 0; selectionFlags |= flagDefault ? C.SELECTION_FLAG_DEFAULT : 0;
selectionFlags |= flagForced ? C.SELECTION_FLAG_FORCED : 0; selectionFlags |= flagForced ? C.SELECTION_FLAG_FORCED : 0;