| /* |
| * Copyright 2017 The WebRTC project authors. All Rights Reserved. |
| * |
| * Use of this source code is governed by a BSD-style license |
| * that can be found in the LICENSE file in the root of the source |
| * tree. An additional intellectual property rights grant can be found |
| * in the file PATENTS. All contributing project authors may |
| * be found in the AUTHORS file in the root of the source tree. |
| */ |
| |
| package org.webrtc; |
| |
| import static org.webrtc.MediaCodecUtils.EXYNOS_PREFIX; |
| import static org.webrtc.MediaCodecUtils.QCOM_PREFIX; |
| |
| import android.media.MediaCodecInfo; |
| import android.media.MediaCodecInfo.CodecCapabilities; |
| import android.media.MediaCodecList; |
| import android.os.Build; |
| import androidx.annotation.Nullable; |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| /** Factory for decoders backed by Android MediaCodec API. */ |
| @SuppressWarnings("deprecation") // API level 16 requires use of deprecated methods. |
| class MediaCodecVideoDecoderFactory implements VideoDecoderFactory { |
| private static final String TAG = "MediaCodecVideoDecoderFactory"; |
| |
| private final @Nullable EglBase.Context sharedContext; |
| private final @Nullable Predicate<MediaCodecInfo> codecAllowedPredicate; |
| |
| /** |
| * MediaCodecVideoDecoderFactory with support of codecs filtering. |
| * |
| * @param sharedContext The textures generated will be accessible from this context. May be null, |
| * this disables texture support. |
| * @param codecAllowedPredicate optional predicate to test if codec allowed. All codecs are |
| * allowed when predicate is not provided. |
| */ |
| public MediaCodecVideoDecoderFactory(@Nullable EglBase.Context sharedContext, |
| @Nullable Predicate<MediaCodecInfo> codecAllowedPredicate) { |
| this.sharedContext = sharedContext; |
| this.codecAllowedPredicate = codecAllowedPredicate; |
| } |
| |
| @Nullable |
| @Override |
| public VideoDecoder createDecoder(VideoCodecInfo codecType) { |
| VideoCodecMimeType type = VideoCodecMimeType.valueOf(codecType.getName()); |
| MediaCodecInfo info = findCodecForType(type); |
| |
| if (info == null) { |
| return null; |
| } |
| |
| CodecCapabilities capabilities = info.getCapabilitiesForType(type.mimeType()); |
| return new AndroidVideoDecoder(new MediaCodecWrapperFactoryImpl(), info.getName(), type, |
| MediaCodecUtils.selectColorFormat(MediaCodecUtils.DECODER_COLOR_FORMATS, capabilities), |
| sharedContext); |
| } |
| |
| @Override |
| public VideoCodecInfo[] getSupportedCodecs() { |
| List<VideoCodecInfo> supportedCodecInfos = new ArrayList<VideoCodecInfo>(); |
| // Generate a list of supported codecs in order of preference: |
| // VP8, VP9, H264 (high profile), H264 (baseline profile), AV1 and H265. |
| for (VideoCodecMimeType type : |
| new VideoCodecMimeType[] {VideoCodecMimeType.VP8, VideoCodecMimeType.VP9, |
| VideoCodecMimeType.H264, VideoCodecMimeType.AV1, VideoCodecMimeType.H265}) { |
| MediaCodecInfo codec = findCodecForType(type); |
| if (codec != null) { |
| String name = type.name(); |
| if (type == VideoCodecMimeType.H264 && isH264HighProfileSupported(codec)) { |
| supportedCodecInfos.add(new VideoCodecInfo( |
| name, MediaCodecUtils.getCodecProperties(type, /* highProfile= */ true))); |
| } |
| |
| supportedCodecInfos.add(new VideoCodecInfo( |
| name, MediaCodecUtils.getCodecProperties(type, /* highProfile= */ false))); |
| } |
| } |
| |
| return supportedCodecInfos.toArray(new VideoCodecInfo[supportedCodecInfos.size()]); |
| } |
| |
| private @Nullable MediaCodecInfo findCodecForType(VideoCodecMimeType type) { |
| for (int i = 0; i < MediaCodecList.getCodecCount(); ++i) { |
| MediaCodecInfo info = null; |
| try { |
| info = MediaCodecList.getCodecInfoAt(i); |
| } catch (IllegalArgumentException e) { |
| Logging.e(TAG, "Cannot retrieve decoder codec info", e); |
| } |
| |
| if (info == null || info.isEncoder()) { |
| continue; |
| } |
| |
| if (isSupportedCodec(info, type)) { |
| return info; |
| } |
| } |
| |
| return null; // No support for this type. |
| } |
| |
| // Returns true if the given MediaCodecInfo indicates a supported encoder for the given type. |
| private boolean isSupportedCodec(MediaCodecInfo info, VideoCodecMimeType type) { |
| if (!MediaCodecUtils.codecSupportsType(info, type)) { |
| return false; |
| } |
| // Check for a supported color format. |
| if (MediaCodecUtils.selectColorFormat( |
| MediaCodecUtils.DECODER_COLOR_FORMATS, info.getCapabilitiesForType(type.mimeType())) |
| == null) { |
| return false; |
| } |
| return isCodecAllowed(info); |
| } |
| |
| private boolean isCodecAllowed(MediaCodecInfo info) { |
| if (codecAllowedPredicate == null) { |
| return true; |
| } |
| return codecAllowedPredicate.test(info); |
| } |
| |
| private boolean isH264HighProfileSupported(MediaCodecInfo info) { |
| String name = info.getName(); |
| // Support H.264 HP decoding on QCOM chips. |
| if (name.startsWith(QCOM_PREFIX)) { |
| return true; |
| } |
| // Support H.264 HP decoding on Exynos chips for Android M and above. |
| if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && name.startsWith(EXYNOS_PREFIX)) { |
| return true; |
| } |
| return false; |
| } |
| } |