diff --git a/RELEASENOTES.md b/RELEASENOTES.md index c115472630..650b200160 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -44,6 +44,8 @@ * Fix CEA-608 subtitles in H.264 MPEG-TS streams not being output (this was broken in `1.5.0-alpha01` by https://github.com/androidx/media/commit/03a205f220ecf7681f85f8a752227e3986e257ff). + * Support CEA-608 subtitles in Dolby Vision content + ([#1820](https://github.com/androidx/media/issues/1820)). * Metadata: * Image: * DRM: diff --git a/libraries/container/src/main/java/androidx/media3/container/NalUnitUtil.java b/libraries/container/src/main/java/androidx/media3/container/NalUnitUtil.java index 080505e219..195566b2d9 100644 --- a/libraries/container/src/main/java/androidx/media3/container/NalUnitUtil.java +++ b/libraries/container/src/main/java/androidx/media3/container/NalUnitUtil.java @@ -15,6 +15,7 @@ */ package androidx.media3.container; +import static androidx.media3.common.MimeTypes.containsCodecsCorrespondingToMimeType; import static com.google.common.math.DoubleMath.log2; import static java.lang.Math.max; import static java.lang.Math.min; @@ -33,6 +34,7 @@ import java.math.RoundingMode; import java.nio.ByteBuffer; import java.util.Arrays; import java.util.List; +import java.util.Objects; /** Utility methods for handling H.264/AVC and H.265/HEVC NAL units. */ @UnstableApi @@ -614,6 +616,24 @@ public final class NalUnitUtil { && ((nalUnitHeaderFirstByte & 0x7E) >> 1) == H265_NAL_UNIT_TYPE_PREFIX_SEI); } + /** + * Returns whether the NAL unit with the specified header contains supplemental enhancement + * information. + * + * @param format The sample {@link Format}. + * @param nalUnitHeaderFirstByte The first byte of nal_unit(). + * @return Whether the NAL unit with the specified header is an SEI NAL unit. False is returned if + * the {@code MimeType} is {@code null}. + */ + public static boolean isNalUnitSei(Format format, byte nalUnitHeaderFirstByte) { + return ((Objects.equals(format.sampleMimeType, MimeTypes.VIDEO_H264) + || containsCodecsCorrespondingToMimeType(format.codecs, MimeTypes.VIDEO_H264)) + && (nalUnitHeaderFirstByte & 0x1F) == H264_NAL_UNIT_TYPE_SEI) + || ((Objects.equals(format.sampleMimeType, MimeTypes.VIDEO_H265) + || containsCodecsCorrespondingToMimeType(format.codecs, MimeTypes.VIDEO_H265)) + && ((nalUnitHeaderFirstByte & 0x7E) >> 1) == H265_NAL_UNIT_TYPE_PREFIX_SEI); + } + /** * Returns the type of the NAL unit in {@code data} that starts at {@code offset}. * diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/FragmentedMp4Extractor.java b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/FragmentedMp4Extractor.java index 8db7798348..3c260c2cce 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/FragmentedMp4Extractor.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/FragmentedMp4Extractor.java @@ -1608,7 +1608,7 @@ public class FragmentedMp4Extractor implements Extractor { output.sampleData(nalPrefix, 1); processSeiNalUnitPayload = ceaTrackOutputs.length > 0 - && NalUnitUtil.isNalUnitSei(track.format.sampleMimeType, nalPrefixData[4]); + && NalUnitUtil.isNalUnitSei(track.format, nalPrefixData[4]); sampleBytesWritten += 5; sampleSize += nalUnitLengthFieldLengthDiff; if (!isSampleDependedOn @@ -1629,7 +1629,12 @@ public class FragmentedMp4Extractor implements Extractor { int unescapedLength = NalUnitUtil.unescapeStream(nalBuffer.getData(), nalBuffer.limit()); // If the format is H.265/HEVC the NAL unit header has two bytes so skip one more byte. - nalBuffer.setPosition(MimeTypes.VIDEO_H265.equals(track.format.sampleMimeType) ? 1 : 0); + nalBuffer.setPosition( + Objects.equals(track.format.sampleMimeType, MimeTypes.VIDEO_H265) + || MimeTypes.containsCodecsCorrespondingToMimeType( + track.format.codecs, MimeTypes.VIDEO_H265) + ? 1 + : 0); nalBuffer.setLimit(unescapedLength); if (track.format.maxNumReorderSamples != Format.NO_VALUE