Fix Dolby Vision fallback to AVC and HEVC
PiperOrigin-RevId: 267979637
This commit is contained in:
parent
0aba89b60e
commit
e21467f653
@ -67,6 +67,7 @@
|
|||||||
* Add `uid` to `Timeline.Window` to uniquely identify window instances.
|
* Add `uid` to `Timeline.Window` to uniquely identify window instances.
|
||||||
* Fix decoder selection for E-AC3 JOC streams
|
* Fix decoder selection for E-AC3 JOC streams
|
||||||
([#6398](https://github.com/google/ExoPlayer/issues/6398)).
|
([#6398](https://github.com/google/ExoPlayer/issues/6398)).
|
||||||
|
* Fix Dolby Vision fallback to AVC and HEVC.
|
||||||
|
|
||||||
### 2.10.4 ###
|
### 2.10.4 ###
|
||||||
|
|
||||||
|
@ -81,8 +81,6 @@ public final class MediaCodecUtil {
|
|||||||
// Dolby Vision.
|
// Dolby Vision.
|
||||||
private static final Map<String, Integer> DOLBY_VISION_STRING_TO_PROFILE;
|
private static final Map<String, Integer> DOLBY_VISION_STRING_TO_PROFILE;
|
||||||
private static final Map<String, Integer> DOLBY_VISION_STRING_TO_LEVEL;
|
private static final Map<String, Integer> DOLBY_VISION_STRING_TO_LEVEL;
|
||||||
private static final String CODEC_ID_DVHE = "dvhe";
|
|
||||||
private static final String CODEC_ID_DVH1 = "dvh1";
|
|
||||||
// AV1.
|
// AV1.
|
||||||
private static final SparseIntArray AV1_LEVEL_NUMBER_TO_CONST;
|
private static final SparseIntArray AV1_LEVEL_NUMBER_TO_CONST;
|
||||||
private static final String CODEC_ID_AV01 = "av01";
|
private static final String CODEC_ID_AV01 = "av01";
|
||||||
@ -245,6 +243,10 @@ public final class MediaCodecUtil {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
String[] parts = format.codecs.split("\\.");
|
String[] parts = format.codecs.split("\\.");
|
||||||
|
// Dolby Vision can use DV, AVC or HEVC codec IDs, so check the MIME type first.
|
||||||
|
if (MimeTypes.VIDEO_DOLBY_VISION.equals(format.sampleMimeType)) {
|
||||||
|
return getDolbyVisionProfileAndLevel(format.codecs, parts);
|
||||||
|
}
|
||||||
switch (parts[0]) {
|
switch (parts[0]) {
|
||||||
case CODEC_ID_AVC1:
|
case CODEC_ID_AVC1:
|
||||||
case CODEC_ID_AVC2:
|
case CODEC_ID_AVC2:
|
||||||
@ -254,9 +256,6 @@ public final class MediaCodecUtil {
|
|||||||
case CODEC_ID_HEV1:
|
case CODEC_ID_HEV1:
|
||||||
case CODEC_ID_HVC1:
|
case CODEC_ID_HVC1:
|
||||||
return getHevcProfileAndLevel(format.codecs, parts);
|
return getHevcProfileAndLevel(format.codecs, parts);
|
||||||
case CODEC_ID_DVHE:
|
|
||||||
case CODEC_ID_DVH1:
|
|
||||||
return getDolbyVisionProfileAndLevel(format.codecs, parts);
|
|
||||||
case CODEC_ID_AV01:
|
case CODEC_ID_AV01:
|
||||||
return getAv1ProfileAndLevel(format.codecs, parts, format.colorInfo);
|
return getAv1ProfileAndLevel(format.codecs, parts, format.colorInfo);
|
||||||
case CODEC_ID_MP4A:
|
case CODEC_ID_MP4A:
|
||||||
|
@ -21,6 +21,7 @@ import android.content.Context;
|
|||||||
import android.graphics.Point;
|
import android.graphics.Point;
|
||||||
import android.media.MediaCodec;
|
import android.media.MediaCodec;
|
||||||
import android.media.MediaCodecInfo.CodecCapabilities;
|
import android.media.MediaCodecInfo.CodecCapabilities;
|
||||||
|
import android.media.MediaCodecInfo.CodecProfileLevel;
|
||||||
import android.media.MediaCrypto;
|
import android.media.MediaCrypto;
|
||||||
import android.media.MediaFormat;
|
import android.media.MediaFormat;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
@ -393,15 +394,17 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
|||||||
format.sampleMimeType, requiresSecureDecoder, requiresTunnelingDecoder);
|
format.sampleMimeType, requiresSecureDecoder, requiresTunnelingDecoder);
|
||||||
decoderInfos = MediaCodecUtil.getDecoderInfosSortedByFormatSupport(decoderInfos, format);
|
decoderInfos = MediaCodecUtil.getDecoderInfosSortedByFormatSupport(decoderInfos, format);
|
||||||
if (MimeTypes.VIDEO_DOLBY_VISION.equals(format.sampleMimeType)) {
|
if (MimeTypes.VIDEO_DOLBY_VISION.equals(format.sampleMimeType)) {
|
||||||
// Fallback to primary decoders for H.265/HEVC or H.264/AVC for the relevant DV profiles.
|
// Fall back to H.264/AVC or H.265/HEVC for the relevant DV profiles.
|
||||||
|
@Nullable
|
||||||
Pair<Integer, Integer> codecProfileAndLevel = MediaCodecUtil.getCodecProfileAndLevel(format);
|
Pair<Integer, Integer> codecProfileAndLevel = MediaCodecUtil.getCodecProfileAndLevel(format);
|
||||||
if (codecProfileAndLevel != null) {
|
if (codecProfileAndLevel != null) {
|
||||||
int profile = codecProfileAndLevel.first;
|
int profile = codecProfileAndLevel.first;
|
||||||
if (profile == 4 || profile == 8) {
|
if (profile == CodecProfileLevel.DolbyVisionProfileDvheDtr
|
||||||
|
|| profile == CodecProfileLevel.DolbyVisionProfileDvheSt) {
|
||||||
decoderInfos.addAll(
|
decoderInfos.addAll(
|
||||||
mediaCodecSelector.getDecoderInfos(
|
mediaCodecSelector.getDecoderInfos(
|
||||||
MimeTypes.VIDEO_H265, requiresSecureDecoder, requiresTunnelingDecoder));
|
MimeTypes.VIDEO_H265, requiresSecureDecoder, requiresTunnelingDecoder));
|
||||||
} else if (profile == 9) {
|
} else if (profile == CodecProfileLevel.DolbyVisionProfileDvavSe) {
|
||||||
decoderInfos.addAll(
|
decoderInfos.addAll(
|
||||||
mediaCodecSelector.getDecoderInfos(
|
mediaCodecSelector.getDecoderInfos(
|
||||||
MimeTypes.VIDEO_H264, requiresSecureDecoder, requiresTunnelingDecoder));
|
MimeTypes.VIDEO_H264, requiresSecureDecoder, requiresTunnelingDecoder));
|
||||||
|
@ -19,6 +19,7 @@ import static com.google.common.truth.Truth.assertThat;
|
|||||||
|
|
||||||
import android.media.MediaCodecInfo;
|
import android.media.MediaCodecInfo;
|
||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
import com.google.android.exoplayer2.Format;
|
import com.google.android.exoplayer2.Format;
|
||||||
@ -34,6 +35,7 @@ public final class MediaCodecUtilTest {
|
|||||||
@Test
|
@Test
|
||||||
public void getCodecProfileAndLevel_handlesVp9Profile1CodecString() {
|
public void getCodecProfileAndLevel_handlesVp9Profile1CodecString() {
|
||||||
assertCodecProfileAndLevelForCodecsString(
|
assertCodecProfileAndLevelForCodecsString(
|
||||||
|
MimeTypes.VIDEO_VP9,
|
||||||
"vp09.01.51",
|
"vp09.01.51",
|
||||||
MediaCodecInfo.CodecProfileLevel.VP9Profile1,
|
MediaCodecInfo.CodecProfileLevel.VP9Profile1,
|
||||||
MediaCodecInfo.CodecProfileLevel.VP9Level51);
|
MediaCodecInfo.CodecProfileLevel.VP9Level51);
|
||||||
@ -42,6 +44,7 @@ public final class MediaCodecUtilTest {
|
|||||||
@Test
|
@Test
|
||||||
public void getCodecProfileAndLevel_handlesVp9Profile2CodecString() {
|
public void getCodecProfileAndLevel_handlesVp9Profile2CodecString() {
|
||||||
assertCodecProfileAndLevelForCodecsString(
|
assertCodecProfileAndLevelForCodecsString(
|
||||||
|
MimeTypes.VIDEO_VP9,
|
||||||
"vp09.02.10",
|
"vp09.02.10",
|
||||||
MediaCodecInfo.CodecProfileLevel.VP9Profile2,
|
MediaCodecInfo.CodecProfileLevel.VP9Profile2,
|
||||||
MediaCodecInfo.CodecProfileLevel.VP9Level1);
|
MediaCodecInfo.CodecProfileLevel.VP9Level1);
|
||||||
@ -51,6 +54,7 @@ public final class MediaCodecUtilTest {
|
|||||||
public void getCodecProfileAndLevel_handlesFullVp9CodecString() {
|
public void getCodecProfileAndLevel_handlesFullVp9CodecString() {
|
||||||
// Example from https://www.webmproject.org/vp9/mp4/#codecs-parameter-string.
|
// Example from https://www.webmproject.org/vp9/mp4/#codecs-parameter-string.
|
||||||
assertCodecProfileAndLevelForCodecsString(
|
assertCodecProfileAndLevelForCodecsString(
|
||||||
|
MimeTypes.VIDEO_VP9,
|
||||||
"vp09.02.10.10.01.09.16.09.01",
|
"vp09.02.10.10.01.09.16.09.01",
|
||||||
MediaCodecInfo.CodecProfileLevel.VP9Profile2,
|
MediaCodecInfo.CodecProfileLevel.VP9Profile2,
|
||||||
MediaCodecInfo.CodecProfileLevel.VP9Level1);
|
MediaCodecInfo.CodecProfileLevel.VP9Level1);
|
||||||
@ -59,6 +63,7 @@ public final class MediaCodecUtilTest {
|
|||||||
@Test
|
@Test
|
||||||
public void getCodecProfileAndLevel_handlesDolbyVisionCodecString() {
|
public void getCodecProfileAndLevel_handlesDolbyVisionCodecString() {
|
||||||
assertCodecProfileAndLevelForCodecsString(
|
assertCodecProfileAndLevelForCodecsString(
|
||||||
|
MimeTypes.VIDEO_DOLBY_VISION,
|
||||||
"dvh1.05.05",
|
"dvh1.05.05",
|
||||||
MediaCodecInfo.CodecProfileLevel.DolbyVisionProfileDvheStn,
|
MediaCodecInfo.CodecProfileLevel.DolbyVisionProfileDvheStn,
|
||||||
MediaCodecInfo.CodecProfileLevel.DolbyVisionLevelFhd60);
|
MediaCodecInfo.CodecProfileLevel.DolbyVisionLevelFhd60);
|
||||||
@ -67,6 +72,7 @@ public final class MediaCodecUtilTest {
|
|||||||
@Test
|
@Test
|
||||||
public void getCodecProfileAndLevel_handlesAv1ProfileMain8CodecString() {
|
public void getCodecProfileAndLevel_handlesAv1ProfileMain8CodecString() {
|
||||||
assertCodecProfileAndLevelForCodecsString(
|
assertCodecProfileAndLevelForCodecsString(
|
||||||
|
MimeTypes.VIDEO_AV1,
|
||||||
"av01.0.10M.08",
|
"av01.0.10M.08",
|
||||||
MediaCodecInfo.CodecProfileLevel.AV1ProfileMain8,
|
MediaCodecInfo.CodecProfileLevel.AV1ProfileMain8,
|
||||||
MediaCodecInfo.CodecProfileLevel.AV1Level42);
|
MediaCodecInfo.CodecProfileLevel.AV1Level42);
|
||||||
@ -75,6 +81,7 @@ public final class MediaCodecUtilTest {
|
|||||||
@Test
|
@Test
|
||||||
public void getCodecProfileAndLevel_handlesAv1ProfileMain10CodecString() {
|
public void getCodecProfileAndLevel_handlesAv1ProfileMain10CodecString() {
|
||||||
assertCodecProfileAndLevelForCodecsString(
|
assertCodecProfileAndLevelForCodecsString(
|
||||||
|
MimeTypes.VIDEO_AV1,
|
||||||
"av01.0.20M.10",
|
"av01.0.20M.10",
|
||||||
MediaCodecInfo.CodecProfileLevel.AV1ProfileMain10,
|
MediaCodecInfo.CodecProfileLevel.AV1ProfileMain10,
|
||||||
MediaCodecInfo.CodecProfileLevel.AV1Level7);
|
MediaCodecInfo.CodecProfileLevel.AV1Level7);
|
||||||
@ -91,7 +98,7 @@ public final class MediaCodecUtilTest {
|
|||||||
Format format =
|
Format format =
|
||||||
Format.createVideoSampleFormat(
|
Format.createVideoSampleFormat(
|
||||||
/* id= */ null,
|
/* id= */ null,
|
||||||
/* sampleMimeType= */ MimeTypes.VIDEO_UNKNOWN,
|
MimeTypes.VIDEO_AV1,
|
||||||
/* codecs= */ "av01.0.21M.10",
|
/* codecs= */ "av01.0.21M.10",
|
||||||
/* bitrate= */ Format.NO_VALUE,
|
/* bitrate= */ Format.NO_VALUE,
|
||||||
/* maxInputSize= */ Format.NO_VALUE,
|
/* maxInputSize= */ Format.NO_VALUE,
|
||||||
@ -122,7 +129,7 @@ public final class MediaCodecUtilTest {
|
|||||||
Format format =
|
Format format =
|
||||||
Format.createVideoSampleFormat(
|
Format.createVideoSampleFormat(
|
||||||
/* id= */ null,
|
/* id= */ null,
|
||||||
/* sampleMimeType= */ MimeTypes.VIDEO_UNKNOWN,
|
MimeTypes.VIDEO_AV1,
|
||||||
/* codecs= */ "av01.0.21M.10",
|
/* codecs= */ "av01.0.21M.10",
|
||||||
/* bitrate= */ Format.NO_VALUE,
|
/* bitrate= */ Format.NO_VALUE,
|
||||||
/* maxInputSize= */ Format.NO_VALUE,
|
/* maxInputSize= */ Format.NO_VALUE,
|
||||||
@ -146,6 +153,7 @@ public final class MediaCodecUtilTest {
|
|||||||
public void getCodecProfileAndLevel_handlesFullAv1CodecString() {
|
public void getCodecProfileAndLevel_handlesFullAv1CodecString() {
|
||||||
// Example from https://aomediacodec.github.io/av1-isobmff/#codecsparam.
|
// Example from https://aomediacodec.github.io/av1-isobmff/#codecsparam.
|
||||||
assertCodecProfileAndLevelForCodecsString(
|
assertCodecProfileAndLevelForCodecsString(
|
||||||
|
MimeTypes.VIDEO_AV1,
|
||||||
"av01.0.04M.10.0.112.09.16.09.0",
|
"av01.0.04M.10.0.112.09.16.09.0",
|
||||||
MediaCodecInfo.CodecProfileLevel.AV1ProfileMain10,
|
MediaCodecInfo.CodecProfileLevel.AV1ProfileMain10,
|
||||||
MediaCodecInfo.CodecProfileLevel.AV1Level3);
|
MediaCodecInfo.CodecProfileLevel.AV1Level3);
|
||||||
@ -186,11 +194,11 @@ public final class MediaCodecUtilTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void assertCodecProfileAndLevelForCodecsString(
|
private static void assertCodecProfileAndLevelForCodecsString(
|
||||||
String codecs, int profile, int level) {
|
String mimeType, String codecs, int profile, int level) {
|
||||||
Format format =
|
Format format =
|
||||||
Format.createVideoSampleFormat(
|
Format.createVideoSampleFormat(
|
||||||
/* id= */ null,
|
/* id= */ null,
|
||||||
/* sampleMimeType= */ MimeTypes.VIDEO_UNKNOWN,
|
mimeType,
|
||||||
/* codecs= */ codecs,
|
/* codecs= */ codecs,
|
||||||
/* bitrate= */ Format.NO_VALUE,
|
/* bitrate= */ Format.NO_VALUE,
|
||||||
/* maxInputSize= */ Format.NO_VALUE,
|
/* maxInputSize= */ Format.NO_VALUE,
|
||||||
@ -203,6 +211,7 @@ public final class MediaCodecUtilTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void assertCodecProfileAndLevelForFormat(Format format, int profile, int level) {
|
private static void assertCodecProfileAndLevelForFormat(Format format, int profile, int level) {
|
||||||
|
@Nullable
|
||||||
Pair<Integer, Integer> codecProfileAndLevel = MediaCodecUtil.getCodecProfileAndLevel(format);
|
Pair<Integer, Integer> codecProfileAndLevel = MediaCodecUtil.getCodecProfileAndLevel(format);
|
||||||
assertThat(codecProfileAndLevel).isNotNull();
|
assertThat(codecProfileAndLevel).isNotNull();
|
||||||
assertThat(codecProfileAndLevel.first).isEqualTo(profile);
|
assertThat(codecProfileAndLevel.first).isEqualTo(profile);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user