/* * 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 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 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 supportedCodecInfos = new ArrayList(); // 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; } }