1 /* 2 * Copyright 2017 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 package org.webrtc; 12 13 import static org.webrtc.MediaCodecUtils.EXYNOS_PREFIX; 14 import static org.webrtc.MediaCodecUtils.QCOM_PREFIX; 15 16 import android.media.MediaCodecInfo; 17 import android.media.MediaCodecInfo.CodecCapabilities; 18 import android.media.MediaCodecList; 19 import android.os.Build; 20 import androidx.annotation.Nullable; 21 import java.util.ArrayList; 22 import java.util.List; 23 24 /** Factory for decoders backed by Android MediaCodec API. */ 25 @SuppressWarnings("deprecation") // API level 16 requires use of deprecated methods. 26 class MediaCodecVideoDecoderFactory implements VideoDecoderFactory { 27 private static final String TAG = "MediaCodecVideoDecoderFactory"; 28 29 private final @Nullable EglBase.Context sharedContext; 30 private final @Nullable Predicate<MediaCodecInfo> codecAllowedPredicate; 31 32 /** 33 * MediaCodecVideoDecoderFactory with support of codecs filtering. 34 * 35 * @param sharedContext The textures generated will be accessible from this context. May be null, 36 * this disables texture support. 37 * @param codecAllowedPredicate optional predicate to test if codec allowed. All codecs are 38 * allowed when predicate is not provided. 39 */ MediaCodecVideoDecoderFactory(@ullable EglBase.Context sharedContext, @Nullable Predicate<MediaCodecInfo> codecAllowedPredicate)40 public MediaCodecVideoDecoderFactory(@Nullable EglBase.Context sharedContext, 41 @Nullable Predicate<MediaCodecInfo> codecAllowedPredicate) { 42 this.sharedContext = sharedContext; 43 this.codecAllowedPredicate = codecAllowedPredicate; 44 } 45 46 @Nullable 47 @Override createDecoder(VideoCodecInfo codecType)48 public VideoDecoder createDecoder(VideoCodecInfo codecType) { 49 VideoCodecMimeType type = VideoCodecMimeType.valueOf(codecType.getName()); 50 MediaCodecInfo info = findCodecForType(type); 51 52 if (info == null) { 53 return null; 54 } 55 56 CodecCapabilities capabilities = info.getCapabilitiesForType(type.mimeType()); 57 return new AndroidVideoDecoder(new MediaCodecWrapperFactoryImpl(), info.getName(), type, 58 MediaCodecUtils.selectColorFormat(MediaCodecUtils.DECODER_COLOR_FORMATS, capabilities), 59 sharedContext); 60 } 61 62 @Override getSupportedCodecs()63 public VideoCodecInfo[] getSupportedCodecs() { 64 List<VideoCodecInfo> supportedCodecInfos = new ArrayList<VideoCodecInfo>(); 65 // Generate a list of supported codecs in order of preference: 66 // VP8, VP9, H264 (high profile), and H264 (baseline profile). 67 for (VideoCodecMimeType type : new VideoCodecMimeType[] {VideoCodecMimeType.VP8, 68 VideoCodecMimeType.VP9, VideoCodecMimeType.H264, VideoCodecMimeType.AV1}) { 69 MediaCodecInfo codec = findCodecForType(type); 70 if (codec != null) { 71 String name = type.name(); 72 if (type == VideoCodecMimeType.H264 && isH264HighProfileSupported(codec)) { 73 supportedCodecInfos.add(new VideoCodecInfo( 74 name, MediaCodecUtils.getCodecProperties(type, /* highProfile= */ true))); 75 } 76 77 supportedCodecInfos.add(new VideoCodecInfo( 78 name, MediaCodecUtils.getCodecProperties(type, /* highProfile= */ false))); 79 } 80 } 81 82 return supportedCodecInfos.toArray(new VideoCodecInfo[supportedCodecInfos.size()]); 83 } 84 findCodecForType(VideoCodecMimeType type)85 private @Nullable MediaCodecInfo findCodecForType(VideoCodecMimeType type) { 86 for (int i = 0; i < MediaCodecList.getCodecCount(); ++i) { 87 MediaCodecInfo info = null; 88 try { 89 info = MediaCodecList.getCodecInfoAt(i); 90 } catch (IllegalArgumentException e) { 91 Logging.e(TAG, "Cannot retrieve decoder codec info", e); 92 } 93 94 if (info == null || info.isEncoder()) { 95 continue; 96 } 97 98 if (isSupportedCodec(info, type)) { 99 return info; 100 } 101 } 102 103 return null; // No support for this type. 104 } 105 106 // Returns true if the given MediaCodecInfo indicates a supported encoder for the given type. isSupportedCodec(MediaCodecInfo info, VideoCodecMimeType type)107 private boolean isSupportedCodec(MediaCodecInfo info, VideoCodecMimeType type) { 108 if (!MediaCodecUtils.codecSupportsType(info, type)) { 109 return false; 110 } 111 // Check for a supported color format. 112 if (MediaCodecUtils.selectColorFormat( 113 MediaCodecUtils.DECODER_COLOR_FORMATS, info.getCapabilitiesForType(type.mimeType())) 114 == null) { 115 return false; 116 } 117 return isCodecAllowed(info); 118 } 119 isCodecAllowed(MediaCodecInfo info)120 private boolean isCodecAllowed(MediaCodecInfo info) { 121 if (codecAllowedPredicate == null) { 122 return true; 123 } 124 return codecAllowedPredicate.test(info); 125 } 126 isH264HighProfileSupported(MediaCodecInfo info)127 private boolean isH264HighProfileSupported(MediaCodecInfo info) { 128 String name = info.getName(); 129 // Support H.264 HP decoding on QCOM chips. 130 if (name.startsWith(QCOM_PREFIX)) { 131 return true; 132 } 133 // Support H.264 HP decoding on Exynos chips for Android M and above. 134 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && name.startsWith(EXYNOS_PREFIX)) { 135 return true; 136 } 137 return false; 138 } 139 } 140