Commit 646b3962 by Ian Bird

Add support for HEVC video in Matroska files

parent cd83bbb7
...@@ -65,6 +65,7 @@ public final class WebmExtractor implements Extractor { ...@@ -65,6 +65,7 @@ public final class WebmExtractor implements Extractor {
private static final String CODEC_ID_VP8 = "V_VP8"; private static final String CODEC_ID_VP8 = "V_VP8";
private static final String CODEC_ID_VP9 = "V_VP9"; private static final String CODEC_ID_VP9 = "V_VP9";
private static final String CODEC_ID_H264 = "V_MPEG4/ISO/AVC"; private static final String CODEC_ID_H264 = "V_MPEG4/ISO/AVC";
private static final String CODEC_ID_H265 = "V_MPEGH/ISO/HEVC";
private static final String CODEC_ID_VORBIS = "A_VORBIS"; private static final String CODEC_ID_VORBIS = "A_VORBIS";
private static final String CODEC_ID_OPUS = "A_OPUS"; private static final String CODEC_ID_OPUS = "A_OPUS";
private static final String CODEC_ID_AAC = "A_AAC"; private static final String CODEC_ID_AAC = "A_AAC";
...@@ -781,7 +782,7 @@ public final class WebmExtractor implements Extractor { ...@@ -781,7 +782,7 @@ public final class WebmExtractor implements Extractor {
} }
size += sampleStrippedBytes.limit(); size += sampleStrippedBytes.limit();
if (CODEC_ID_H264.equals(format.codecId)) { if (CODEC_ID_H264.equals(format.codecId) || CODEC_ID_H265.equals(format.codecId)) {
// TODO: Deduplicate with Mp4Extractor. // TODO: Deduplicate with Mp4Extractor.
// Zero the top three bytes of the array that we'll use to parse nal unit lengths, in case // Zero the top three bytes of the array that we'll use to parse nal unit lengths, in case
...@@ -935,6 +936,7 @@ public final class WebmExtractor implements Extractor { ...@@ -935,6 +936,7 @@ public final class WebmExtractor implements Extractor {
return CODEC_ID_VP8.equals(codecId) return CODEC_ID_VP8.equals(codecId)
|| CODEC_ID_VP9.equals(codecId) || CODEC_ID_VP9.equals(codecId)
|| CODEC_ID_H264.equals(codecId) || CODEC_ID_H264.equals(codecId)
|| CODEC_ID_H265.equals(codecId)
|| CODEC_ID_OPUS.equals(codecId) || CODEC_ID_OPUS.equals(codecId)
|| CODEC_ID_VORBIS.equals(codecId) || CODEC_ID_VORBIS.equals(codecId)
|| CODEC_ID_AAC.equals(codecId) || CODEC_ID_AAC.equals(codecId)
...@@ -1045,6 +1047,13 @@ public final class WebmExtractor implements Extractor { ...@@ -1045,6 +1047,13 @@ public final class WebmExtractor implements Extractor {
initializationData = h264Data.first; initializationData = h264Data.first;
nalUnitLengthFieldLength = h264Data.second; nalUnitLengthFieldLength = h264Data.second;
break; break;
case CODEC_ID_H265:
mimeType = MimeTypes.VIDEO_H265;
Pair<List<byte[]>, Integer> hevcData = parseHEVCCodecPrivate(
new ParsableByteArray(codecPrivate));
initializationData = hevcData.first;
nalUnitLengthFieldLength = hevcData.second;
break;
case CODEC_ID_VORBIS: case CODEC_ID_VORBIS:
mimeType = MimeTypes.AUDIO_VORBIS; mimeType = MimeTypes.AUDIO_VORBIS;
maxInputSize = VORBIS_MAX_INPUT_SIZE; maxInputSize = VORBIS_MAX_INPUT_SIZE;
...@@ -1113,6 +1122,58 @@ public final class WebmExtractor implements Extractor { ...@@ -1113,6 +1122,58 @@ public final class WebmExtractor implements Extractor {
} }
/** /**
* Builds initialization data for a {@link MediaFormat} from H.265 (HEVC) codec private data.
*
* @return The initialization data for the {@link MediaFormat}.
* @throws ParserException If the initialization data could not be built.
*/
private static Pair<List<byte[]>, Integer> parseHEVCCodecPrivate(ParsableByteArray parent)
throws ParserException {
try {
// TODO: Deduplicate with AtomParsers.parseAvcCFromParent.
parent.setPosition(21);
int lengthSizeMinusOne = parent.readUnsignedByte() & 0x03;
// Calculate the combined size of all VPS/SPS/PPS bitstreams.
int numberOfArrays = parent.readUnsignedByte();
int csdLength = 0;
int csdStartPosition = parent.getPosition();
for (int i = 0; i < numberOfArrays; i++) {
parent.skipBytes(1); // completeness (1), nal_unit_type (7)
int numberOfNalUnits = parent.readUnsignedShort();
for (int j = 0; j < numberOfNalUnits; j++) {
int nalUnitLength = parent.readUnsignedShort();
csdLength += 4 + nalUnitLength; // Start code and NAL unit.
parent.skipBytes(nalUnitLength);
}
}
// Concatenate the codec-specific data into a single buffer.
parent.setPosition(csdStartPosition);
byte[] buffer = new byte[csdLength];
int bufferPosition = 0;
for (int i = 0; i < numberOfArrays; i++) {
parent.skipBytes(1); // completeness (1), nal_unit_type (7)
int numberOfNalUnits = parent.readUnsignedShort();
for (int j = 0; j < numberOfNalUnits; j++) {
int nalUnitLength = parent.readUnsignedShort();
System.arraycopy(NalUnitUtil.NAL_START_CODE, 0, buffer, bufferPosition,
NalUnitUtil.NAL_START_CODE.length);
bufferPosition += NalUnitUtil.NAL_START_CODE.length;
System.arraycopy(parent.data, parent.getPosition(), buffer, bufferPosition, nalUnitLength);
bufferPosition += nalUnitLength;
parent.skipBytes(nalUnitLength);
}
}
List<byte[]> initializationData = csdLength == 0 ? null : Collections.singletonList(buffer);
return Pair.create(initializationData, lengthSizeMinusOne + 1);
} catch (ArrayIndexOutOfBoundsException e) {
throw new ParserException("Error parsing vorbis codec private");
}
}
/**
* Builds initialization data for a {@link MediaFormat} from Vorbis codec private data. * Builds initialization data for a {@link MediaFormat} from Vorbis codec private data.
* *
* @return The initialization data for the {@link MediaFormat}. * @return The initialization data for the {@link MediaFormat}.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment