diff options
Diffstat (limited to 'tuner/src/com/google/android/exoplayer/MediaSoftwareCodecUtil.java')
-rw-r--r-- | tuner/src/com/google/android/exoplayer/MediaSoftwareCodecUtil.java | 273 |
1 files changed, 273 insertions, 0 deletions
diff --git a/tuner/src/com/google/android/exoplayer/MediaSoftwareCodecUtil.java b/tuner/src/com/google/android/exoplayer/MediaSoftwareCodecUtil.java new file mode 100644 index 00000000..cf74f106 --- /dev/null +++ b/tuner/src/com/google/android/exoplayer/MediaSoftwareCodecUtil.java @@ -0,0 +1,273 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.android.exoplayer; + +import android.annotation.TargetApi; +import android.media.MediaCodecInfo; +import android.media.MediaCodecList; +import android.text.TextUtils; +import android.util.Log; +import android.util.Pair; +import com.google.android.exoplayer.util.MimeTypes; +import java.util.HashMap; + +/** + * Mostly copied from {@link com.google.android.exoplayer.MediaCodecUtil} in order to choose + * software codec over hardware codec. + */ +public class MediaSoftwareCodecUtil { + private static final String TAG = "MediaSoftwareCodecUtil"; + + /** + * Thrown when an error occurs querying the device for its underlying media capabilities. + * + * <p>Such failures are not expected in normal operation and are normally temporary (e.g. if the + * mediaserver process has crashed and is yet to restart). + */ + public static class DecoderQueryException extends Exception { + + private DecoderQueryException(Throwable cause) { + super("Failed to query underlying media codecs", cause); + } + } + + private static final HashMap<CodecKey, Pair<String, MediaCodecInfo.CodecCapabilities>> + sSwCodecs = new HashMap<>(); + + /** Gets information about the software decoder that will be used for a given mime type. */ + public static DecoderInfo getSoftwareDecoderInfo(String mimeType, boolean secure) + throws DecoderQueryException { + // TODO: Add a test for this method. + Pair<String, MediaCodecInfo.CodecCapabilities> info = + getMediaSoftwareCodecInfo(mimeType, secure); + if (info == null) { + return null; + } + return new DecoderInfo(info.first, info.second); + } + + /** Returns the name of the software decoder and its capabilities for the given mimeType. */ + private static synchronized Pair<String, MediaCodecInfo.CodecCapabilities> + getMediaSoftwareCodecInfo(String mimeType, boolean secure) + throws DecoderQueryException { + CodecKey key = new CodecKey(mimeType, secure); + if (sSwCodecs.containsKey(key)) { + return sSwCodecs.get(key); + } + MediaCodecListCompat mediaCodecList = new MediaCodecListCompatV21(secure); + Pair<String, MediaCodecInfo.CodecCapabilities> codecInfo = + getMediaSoftwareCodecInfo(key, mediaCodecList); + if (secure && codecInfo == null) { + // Some devices don't list secure decoders on API level 21. Try the legacy path. + mediaCodecList = new MediaCodecListCompatV16(); + codecInfo = getMediaSoftwareCodecInfo(key, mediaCodecList); + if (codecInfo != null) { + Log.w( + TAG, + "MediaCodecList API didn't list secure decoder for: " + + mimeType + + ". Assuming: " + + codecInfo.first); + } + } + return codecInfo; + } + + private static Pair<String, MediaCodecInfo.CodecCapabilities> getMediaSoftwareCodecInfo( + CodecKey key, MediaCodecListCompat mediaCodecList) throws DecoderQueryException { + try { + return getMediaSoftwareCodecInfoInternal(key, mediaCodecList); + } catch (Exception e) { + // If the underlying mediaserver is in a bad state, we may catch an + // IllegalStateException or an IllegalArgumentException here. + throw new DecoderQueryException(e); + } + } + + private static Pair<String, MediaCodecInfo.CodecCapabilities> getMediaSoftwareCodecInfoInternal( + CodecKey key, MediaCodecListCompat mediaCodecList) { + String mimeType = key.mimeType; + int numberOfCodecs = mediaCodecList.getCodecCount(); + boolean secureDecodersExplicit = mediaCodecList.secureDecodersExplicit(); + // Note: MediaCodecList is sorted by the framework such that the best decoders come first. + for (int i = 0; i < numberOfCodecs; i++) { + MediaCodecInfo info = mediaCodecList.getCodecInfoAt(i); + String codecName = info.getName(); + if (!info.isEncoder() + && codecName.startsWith("OMX.google.") + && (secureDecodersExplicit || !codecName.endsWith(".secure"))) { + String[] supportedTypes = info.getSupportedTypes(); + for (String supportedType : supportedTypes) { + if (supportedType.equalsIgnoreCase(mimeType)) { + MediaCodecInfo.CodecCapabilities capabilities = + info.getCapabilitiesForType(supportedType); + boolean secure = + mediaCodecList.isSecurePlaybackSupported( + key.mimeType, capabilities); + if (!secureDecodersExplicit) { + // Cache variants for both insecure and (if we think it's supported) + // secure playback. + sSwCodecs.put( + key.secure ? new CodecKey(mimeType, false) : key, + Pair.create(codecName, capabilities)); + if (secure) { + sSwCodecs.put( + key.secure ? key : new CodecKey(mimeType, true), + Pair.create(codecName + ".secure", capabilities)); + } + } else { + // Only cache this variant. If both insecure and secure decoders are + // available, they should both be listed separately. + sSwCodecs.put( + key.secure == secure ? key : new CodecKey(mimeType, secure), + Pair.create(codecName, capabilities)); + } + if (sSwCodecs.containsKey(key)) { + return sSwCodecs.get(key); + } + } + } + } + } + sSwCodecs.put(key, null); + return null; + } + + private interface MediaCodecListCompat { + + /** Returns the number of codecs in the list. */ + int getCodecCount(); + + /** + * Returns the info at the specified index in the list. + * + * @param index The index. + */ + MediaCodecInfo getCodecInfoAt(int index); + + /** Returns whether secure decoders are explicitly listed, if present. */ + boolean secureDecodersExplicit(); + + /** + * Returns true if secure playback is supported for the given {@link + * android.media.MediaCodecInfo.CodecCapabilities}, which should have been obtained from a + * {@link MediaCodecInfo} obtained from this list. + */ + boolean isSecurePlaybackSupported( + String mimeType, MediaCodecInfo.CodecCapabilities capabilities); + } + + @TargetApi(21) + private static final class MediaCodecListCompatV21 implements MediaCodecListCompat { + + private final int codecKind; + + private MediaCodecInfo[] mediaCodecInfos; + + public MediaCodecListCompatV21(boolean includeSecure) { + codecKind = includeSecure ? MediaCodecList.ALL_CODECS : MediaCodecList.REGULAR_CODECS; + } + + @Override + public int getCodecCount() { + ensureMediaCodecInfosInitialized(); + return mediaCodecInfos.length; + } + + @Override + public MediaCodecInfo getCodecInfoAt(int index) { + ensureMediaCodecInfosInitialized(); + return mediaCodecInfos[index]; + } + + @Override + public boolean secureDecodersExplicit() { + return true; + } + + @Override + public boolean isSecurePlaybackSupported( + String mimeType, MediaCodecInfo.CodecCapabilities capabilities) { + return capabilities.isFeatureSupported( + MediaCodecInfo.CodecCapabilities.FEATURE_SecurePlayback); + } + + private void ensureMediaCodecInfosInitialized() { + if (mediaCodecInfos == null) { + mediaCodecInfos = new MediaCodecList(codecKind).getCodecInfos(); + } + } + } + + @SuppressWarnings("deprecation") + private static final class MediaCodecListCompatV16 implements MediaCodecListCompat { + + @Override + public int getCodecCount() { + return MediaCodecList.getCodecCount(); + } + + @Override + public MediaCodecInfo getCodecInfoAt(int index) { + return MediaCodecList.getCodecInfoAt(index); + } + + @Override + public boolean secureDecodersExplicit() { + return false; + } + + @Override + public boolean isSecurePlaybackSupported( + String mimeType, MediaCodecInfo.CodecCapabilities capabilities) { + // Secure decoders weren't explicitly listed prior to API level 21. We assume that + // a secure H264 decoder exists. + return MimeTypes.VIDEO_H264.equals(mimeType); + } + } + + private static final class CodecKey { + + public final String mimeType; + public final boolean secure; + + public CodecKey(String mimeType, boolean secure) { + this.mimeType = mimeType; + this.secure = secure; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((mimeType == null) ? 0 : mimeType.hashCode()); + result = 2 * result + (secure ? 0 : 1); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof CodecKey)) { + return false; + } + CodecKey other = (CodecKey) obj; + return TextUtils.equals(mimeType, other.mimeType) && secure == other.secure; + } + } +} |