Fix Dolby Vision fallback to AVC and HEVC

PiperOrigin-RevId: 267979637
This commit is contained in:
andrewlewis 2019-09-09 13:58:59 +01:00 committed by Oliver Woodman
parent 0aba89b60e
commit e21467f653
4 changed files with 24 additions and 12 deletions

View File

@ -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 ###

View File

@ -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:

View File

@ -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));

View File

@ -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);