mirror of
https://github.com/androidx/media.git
synced 2025-05-03 21:57:46 +08:00
Add test to query device capabilities.
PiperOrigin-RevId: 439861685
This commit is contained in:
parent
b5eba24e1f
commit
61a20d5f68
@ -21,6 +21,7 @@ import android.content.Context;
|
|||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import com.google.android.exoplayer2.Format;
|
import com.google.android.exoplayer2.Format;
|
||||||
import com.google.android.exoplayer2.util.Log;
|
import com.google.android.exoplayer2.util.Log;
|
||||||
|
import com.google.android.exoplayer2.util.Util;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileWriter;
|
import java.io.FileWriter;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -126,14 +127,16 @@ public final class AndroidTestUtil {
|
|||||||
* @param testId A unique identifier for the transformer test run.
|
* @param testId A unique identifier for the transformer test run.
|
||||||
* @param testJson A {@link JSONObject} containing a summary of the test run.
|
* @param testJson A {@link JSONObject} containing a summary of the test run.
|
||||||
*/
|
*/
|
||||||
/* package */ static void writeTestSummaryToFile(
|
public static void writeTestSummaryToFile(Context context, String testId, JSONObject testJson)
|
||||||
Context context, String testId, JSONObject testJson) throws IOException, JSONException {
|
throws IOException, JSONException {
|
||||||
testJson.put("testId", testId).put("device", getDeviceDetailsAsJsonObject());
|
testJson.put("testId", testId).put("device", getDeviceDetailsAsJsonObject());
|
||||||
|
|
||||||
String analysisContents = testJson.toString(/* indentSpaces= */ 2);
|
String analysisContents = testJson.toString(/* indentSpaces= */ 2);
|
||||||
|
|
||||||
// Log contents as well as writing to file, for easier visibility on individual device testing.
|
// Log contents as well as writing to file, for easier visibility on individual device testing.
|
||||||
Log.i(testId, analysisContents);
|
for (String line : Util.split(analysisContents, "\n")) {
|
||||||
|
Log.i(testId, line);
|
||||||
|
}
|
||||||
|
|
||||||
File analysisFile = createExternalCacheFile(context, /* fileName= */ testId + "-result.txt");
|
File analysisFile = createExternalCacheFile(context, /* fileName= */ testId + "-result.txt");
|
||||||
try (FileWriter fileWriter = new FileWriter(analysisFile)) {
|
try (FileWriter fileWriter = new FileWriter(analysisFile)) {
|
||||||
|
@ -0,0 +1,197 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022 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.exoplayer2.transformer.mh.analysis;
|
||||||
|
|
||||||
|
import static android.media.MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_CBR;
|
||||||
|
import static android.media.MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_CBR_FD;
|
||||||
|
import static android.media.MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_CQ;
|
||||||
|
import static android.media.MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_VBR;
|
||||||
|
|
||||||
|
import android.media.MediaCodecInfo;
|
||||||
|
import android.util.Pair;
|
||||||
|
import android.util.Range;
|
||||||
|
import android.util.Size;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
import com.google.android.exoplayer2.transformer.AndroidTestUtil;
|
||||||
|
import com.google.android.exoplayer2.transformer.EncoderUtil;
|
||||||
|
import com.google.android.exoplayer2.util.Util;
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
/** An analysis test to log encoder capabilities on a device. */
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
public class EncoderCapabilityAnalysisTest {
|
||||||
|
|
||||||
|
// TODO(b/228167357): Remove after bumping compileApiVersion to 33.
|
||||||
|
/** Re-definition of {@code MediaCodecInfo.CodecCapabilities.FEATURE_HdrEditing} in API33. */
|
||||||
|
private static final String FEATURE_HdrEditing = "hdr-editing";
|
||||||
|
/**
|
||||||
|
* Re-definition of {@code MediaCodecInfo.CodecCapabilities.FEATURE_EncodingStatistics} in API33.
|
||||||
|
*/
|
||||||
|
private static final String FEATURE_EncodingStatistics = "encoding-statistics";
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void logEncoderCapabilities() throws Exception {
|
||||||
|
ImmutableSet<String> supportedVideoMimeTypes = EncoderUtil.getSupportedVideoMimeTypes();
|
||||||
|
|
||||||
|
// Map from MIME type to a list of maps from capability name to value.
|
||||||
|
LinkedHashMap<String, List<Map<String, Object>>> mimeTypeToEncoderInfo = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
for (String mimeType : supportedVideoMimeTypes) {
|
||||||
|
ImmutableList<MediaCodecInfo> encoderInfos = EncoderUtil.getSupportedEncoders(mimeType);
|
||||||
|
ArrayList<Map<String, Object>> encoderCapabilitiesForMimeType = new ArrayList<>();
|
||||||
|
for (MediaCodecInfo encoderInfo : encoderInfos) {
|
||||||
|
LinkedHashMap<String, Object> capabilities = new LinkedHashMap<>();
|
||||||
|
capabilities.put("encoder_name", encoderInfo.getName());
|
||||||
|
|
||||||
|
capabilities.put(
|
||||||
|
"is_software_encoder", !EncoderUtil.isHardwareAccelerated(encoderInfo, mimeType));
|
||||||
|
|
||||||
|
// Bitrate modes.
|
||||||
|
capabilities.put(
|
||||||
|
"supports_vbr",
|
||||||
|
EncoderUtil.isBitrateModeSupported(encoderInfo, mimeType, BITRATE_MODE_VBR));
|
||||||
|
capabilities.put(
|
||||||
|
"supports_cbr",
|
||||||
|
EncoderUtil.isBitrateModeSupported(encoderInfo, mimeType, BITRATE_MODE_CBR));
|
||||||
|
capabilities.put(
|
||||||
|
"supports_cq",
|
||||||
|
EncoderUtil.isBitrateModeSupported(encoderInfo, mimeType, BITRATE_MODE_CQ));
|
||||||
|
capabilities.put(
|
||||||
|
"supports_cbr_fd",
|
||||||
|
EncoderUtil.isBitrateModeSupported(encoderInfo, mimeType, BITRATE_MODE_CBR_FD));
|
||||||
|
|
||||||
|
capabilities.put(
|
||||||
|
"supported_bitrate_range",
|
||||||
|
rangeToString(EncoderUtil.getSupportedBitrateRange(encoderInfo, mimeType)));
|
||||||
|
|
||||||
|
// Resolution support.
|
||||||
|
Pair<Range<Integer>, Range<Integer>> supportedResolutionRanges =
|
||||||
|
EncoderUtil.getSupportedResolutionRanges(encoderInfo, mimeType);
|
||||||
|
capabilities.put("supported_widths_range", rangeToString(supportedResolutionRanges.first));
|
||||||
|
capabilities.put(
|
||||||
|
"supported_heights_range", rangeToString(supportedResolutionRanges.second));
|
||||||
|
|
||||||
|
checkResolutionSupport(
|
||||||
|
encoderInfo, mimeType, capabilities, /* width= */ 1280, /* height= */ 720);
|
||||||
|
checkResolutionSupport(
|
||||||
|
encoderInfo, mimeType, capabilities, /* width= */ 1920, /* height= */ 1080);
|
||||||
|
checkResolutionSupport(
|
||||||
|
encoderInfo, mimeType, capabilities, /* width= */ 2560, /* height= */ 1440);
|
||||||
|
checkResolutionSupport(
|
||||||
|
encoderInfo, mimeType, capabilities, /* width= */ 3840, /* height= */ 2160);
|
||||||
|
|
||||||
|
checkProfileLevelSupport(encoderInfo, mimeType, capabilities);
|
||||||
|
|
||||||
|
capabilities.put(
|
||||||
|
"supported_color_profiles",
|
||||||
|
EncoderUtil.getSupportedColorFormats(encoderInfo, mimeType));
|
||||||
|
|
||||||
|
capabilities.put(
|
||||||
|
"max_supported_instances",
|
||||||
|
Util.SDK_INT >= 23 ? EncoderUtil.getMaxSupportedInstances(encoderInfo, mimeType) : -1);
|
||||||
|
|
||||||
|
capabilities.put(
|
||||||
|
"supports_qp_bounds",
|
||||||
|
Util.SDK_INT >= 31
|
||||||
|
&& EncoderUtil.isFeatureSupported(
|
||||||
|
encoderInfo, mimeType, MediaCodecInfo.CodecCapabilities.FEATURE_QpBounds));
|
||||||
|
|
||||||
|
capabilities.put(
|
||||||
|
"supports_hdr_editing",
|
||||||
|
Util.SDK_INT >= 33
|
||||||
|
&& EncoderUtil.isFeatureSupported(encoderInfo, mimeType, FEATURE_HdrEditing));
|
||||||
|
|
||||||
|
capabilities.put(
|
||||||
|
"supports_encoding_statistics",
|
||||||
|
Util.SDK_INT >= 33
|
||||||
|
&& EncoderUtil.isFeatureSupported(
|
||||||
|
encoderInfo, mimeType, FEATURE_EncodingStatistics));
|
||||||
|
|
||||||
|
encoderCapabilitiesForMimeType.add(capabilities);
|
||||||
|
}
|
||||||
|
mimeTypeToEncoderInfo.put(mimeType, encoderCapabilitiesForMimeType);
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONObject resultJson = new JSONObject();
|
||||||
|
resultJson.put("encoder_capabilities", JSONObject.wrap(mimeTypeToEncoderInfo));
|
||||||
|
AndroidTestUtil.writeTestSummaryToFile(
|
||||||
|
ApplicationProvider.getApplicationContext(),
|
||||||
|
/* testId= */ "encoderCapabilityAnalysisTest",
|
||||||
|
resultJson);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void checkResolutionSupport(
|
||||||
|
MediaCodecInfo encoder,
|
||||||
|
String mimeType,
|
||||||
|
Map<String, Object> capabilities,
|
||||||
|
int width,
|
||||||
|
int height) {
|
||||||
|
Range<Integer> supportedWidths =
|
||||||
|
EncoderUtil.getSupportedResolutionRanges(encoder, mimeType).first;
|
||||||
|
@Nullable Range<Integer> supportedHeights = null;
|
||||||
|
if (supportedWidths.contains(width)) {
|
||||||
|
supportedHeights = EncoderUtil.getSupportedHeights(encoder, mimeType, width);
|
||||||
|
}
|
||||||
|
capabilities.put(
|
||||||
|
Util.formatInvariant("supported_heights_for_%d", width), rangeToString(supportedHeights));
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
Size supportedResolution = EncoderUtil.getSupportedResolution(encoder, mimeType, width, height);
|
||||||
|
if (supportedResolution == null) {
|
||||||
|
supportedResolution = new Size(/* width= */ 0, /* height= */ 0);
|
||||||
|
}
|
||||||
|
capabilities.put(
|
||||||
|
Util.formatInvariant("supports_%dx%d", width, height),
|
||||||
|
(supportedResolution.getWidth() == width && supportedResolution.getHeight() == height));
|
||||||
|
capabilities.put(
|
||||||
|
Util.formatInvariant("fallback_%dx%d", width, height), sizeToString(supportedResolution));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void checkProfileLevelSupport(
|
||||||
|
MediaCodecInfo encoder, String mimeType, Map<String, Object> capabilities) {
|
||||||
|
LinkedHashMap<String, String> profileToHighestSupportedLevel = new LinkedHashMap<>();
|
||||||
|
ImmutableSet<Integer> supportedEncodingProfiles =
|
||||||
|
EncoderUtil.findSupportedEncodingProfiles(encoder, mimeType);
|
||||||
|
for (int profile : supportedEncodingProfiles) {
|
||||||
|
profileToHighestSupportedLevel.put(
|
||||||
|
String.valueOf(profile),
|
||||||
|
String.valueOf(
|
||||||
|
EncoderUtil.findHighestSupportedEncodingLevel(encoder, mimeType, profile)));
|
||||||
|
}
|
||||||
|
capabilities.put("supported_profile_levels", profileToHighestSupportedLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String rangeToString(@Nullable Range<Integer> range) {
|
||||||
|
return range == null
|
||||||
|
? "0-0"
|
||||||
|
: Util.formatInvariant("%d-%d", range.getLower(), range.getUpper());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String sizeToString(@Nullable Size size) {
|
||||||
|
return size == null ? "0x0" : Util.formatInvariant("%dx%d", size.getWidth(), size.getHeight());
|
||||||
|
}
|
||||||
|
}
|
@ -267,7 +267,7 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
|
|||||||
|
|
||||||
MediaCodecInfo pickedEncoder = filteredEncoders.get(0);
|
MediaCodecInfo pickedEncoder = filteredEncoders.get(0);
|
||||||
int closestSupportedBitrate =
|
int closestSupportedBitrate =
|
||||||
EncoderUtil.getClosestSupportedBitrate(pickedEncoder, mimeType, requestedBitrate);
|
EncoderUtil.getSupportedBitrateRange(pickedEncoder, mimeType).clamp(requestedBitrate);
|
||||||
VideoEncoderSettings.Builder supportedEncodingSettingBuilder =
|
VideoEncoderSettings.Builder supportedEncodingSettingBuilder =
|
||||||
videoEncoderSettings.buildUpon().setBitrate(closestSupportedBitrate);
|
videoEncoderSettings.buildUpon().setBitrate(closestSupportedBitrate);
|
||||||
|
|
||||||
@ -320,7 +320,7 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
|
|||||||
encoders,
|
encoders,
|
||||||
/* cost= */ (encoderInfo) -> {
|
/* cost= */ (encoderInfo) -> {
|
||||||
int achievableBitrate =
|
int achievableBitrate =
|
||||||
EncoderUtil.getClosestSupportedBitrate(encoderInfo, mimeType, requestedBitrate);
|
EncoderUtil.getSupportedBitrateRange(encoderInfo, mimeType).clamp(requestedBitrate);
|
||||||
return abs(achievableBitrate - requestedBitrate);
|
return abs(achievableBitrate - requestedBitrate);
|
||||||
},
|
},
|
||||||
/* filterName= */ "bitrate");
|
/* filterName= */ "bitrate");
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package com.google.android.exoplayer2.transformer;
|
package com.google.android.exoplayer2.transformer;
|
||||||
|
|
||||||
|
import static com.google.android.exoplayer2.util.Assertions.checkNotNull;
|
||||||
import static java.lang.Math.max;
|
import static java.lang.Math.max;
|
||||||
import static java.lang.Math.round;
|
import static java.lang.Math.round;
|
||||||
|
|
||||||
@ -23,6 +24,8 @@ import android.media.MediaCodec;
|
|||||||
import android.media.MediaCodecInfo;
|
import android.media.MediaCodecInfo;
|
||||||
import android.media.MediaCodecList;
|
import android.media.MediaCodecList;
|
||||||
import android.media.MediaFormat;
|
import android.media.MediaFormat;
|
||||||
|
import android.util.Pair;
|
||||||
|
import android.util.Range;
|
||||||
import android.util.Size;
|
import android.util.Size;
|
||||||
import androidx.annotation.DoNotInline;
|
import androidx.annotation.DoNotInline;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
@ -32,9 +35,12 @@ import com.google.android.exoplayer2.util.MediaFormatUtil;
|
|||||||
import com.google.android.exoplayer2.util.MimeTypes;
|
import com.google.android.exoplayer2.util.MimeTypes;
|
||||||
import com.google.android.exoplayer2.util.Util;
|
import com.google.android.exoplayer2.util.Util;
|
||||||
import com.google.common.base.Ascii;
|
import com.google.common.base.Ascii;
|
||||||
|
import com.google.common.base.Supplier;
|
||||||
|
import com.google.common.base.Suppliers;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import java.util.ArrayList;
|
import com.google.common.collect.ImmutableListMultimap;
|
||||||
import java.util.List;
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import com.google.common.primitives.Ints;
|
||||||
|
|
||||||
/** Utility methods for {@link MediaCodec} encoders. */
|
/** Utility methods for {@link MediaCodec} encoders. */
|
||||||
public final class EncoderUtil {
|
public final class EncoderUtil {
|
||||||
@ -42,26 +48,47 @@ public final class EncoderUtil {
|
|||||||
/** A value to indicate the encoding level is not set. */
|
/** A value to indicate the encoding level is not set. */
|
||||||
public static final int LEVEL_UNSET = Format.NO_VALUE;
|
public static final int LEVEL_UNSET = Format.NO_VALUE;
|
||||||
|
|
||||||
private static final List<MediaCodecInfo> encoders = new ArrayList<>();
|
private static final Supplier<ImmutableListMultimap<String, MediaCodecInfo>>
|
||||||
|
MIME_TYPE_TO_ENCODERS = Suppliers.memoize(EncoderUtil::populateEncoderInfos);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a list of {@linkplain MediaCodecInfo encoders} that support the given {@code mimeType},
|
* Returns a list of {@linkplain MediaCodecInfo encoders} that support the given {@code mimeType},
|
||||||
* or an empty list if there is none.
|
* or an empty list if there is none.
|
||||||
*/
|
*/
|
||||||
public static ImmutableList<MediaCodecInfo> getSupportedEncoders(String mimeType) {
|
public static ImmutableList<MediaCodecInfo> getSupportedEncoders(String mimeType) {
|
||||||
maybePopulateEncoderInfos();
|
return checkNotNull(MIME_TYPE_TO_ENCODERS.get()).get(Ascii.toLowerCase(mimeType));
|
||||||
|
}
|
||||||
|
|
||||||
ImmutableList.Builder<MediaCodecInfo> availableEncoders = new ImmutableList.Builder<>();
|
/** Returns a list of video {@linkplain MimeTypes MIME types} that can be encoded. */
|
||||||
for (int i = 0; i < encoders.size(); i++) {
|
public static ImmutableSet<String> getSupportedVideoMimeTypes() {
|
||||||
MediaCodecInfo encoderInfo = encoders.get(i);
|
return checkNotNull(MIME_TYPE_TO_ENCODERS.get()).keySet();
|
||||||
String[] supportedMimeTypes = encoderInfo.getSupportedTypes();
|
}
|
||||||
for (String supportedMimeType : supportedMimeTypes) {
|
|
||||||
if (Ascii.equalsIgnoreCase(supportedMimeType, mimeType)) {
|
/**
|
||||||
availableEncoders.add(encoderInfo);
|
* Returns a {@link Range} of supported heights for the given {@link MediaCodecInfo encoder},
|
||||||
}
|
* {@linkplain MimeTypes MIME type} and {@code width}.
|
||||||
}
|
*
|
||||||
}
|
* @throws IllegalArgumentException When the width is not in the range of {@linkplain
|
||||||
return availableEncoders.build();
|
* #getSupportedResolutionRanges supported widths}.
|
||||||
|
*/
|
||||||
|
public static Range<Integer> getSupportedHeights(
|
||||||
|
MediaCodecInfo encoderInfo, String mimeType, int width) {
|
||||||
|
return encoderInfo
|
||||||
|
.getCapabilitiesForType(mimeType)
|
||||||
|
.getVideoCapabilities()
|
||||||
|
.getSupportedHeightsFor(width);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a {@link Pair} of supported width and height {@link Range ranges} for the given {@link
|
||||||
|
* MediaCodecInfo encoder} and {@linkplain MimeTypes MIME type}.
|
||||||
|
*/
|
||||||
|
public static Pair<Range<Integer>, Range<Integer>> getSupportedResolutionRanges(
|
||||||
|
MediaCodecInfo encoderInfo, String mimeType) {
|
||||||
|
MediaCodecInfo.VideoCapabilities videoCapabilities =
|
||||||
|
encoderInfo.getCapabilitiesForType(mimeType).getVideoCapabilities();
|
||||||
|
return Pair.create(
|
||||||
|
videoCapabilities.getSupportedWidths(), videoCapabilities.getSupportedHeights());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -130,6 +157,22 @@ public final class EncoderUtil {
|
|||||||
return videoEncoderCapabilities.isSizeSupported(width, height) ? new Size(width, height) : null;
|
return videoEncoderCapabilities.isSizeSupported(width, height) ? new Size(width, height) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a {@link ImmutableSet set} of supported {@linkplain MediaCodecInfo.CodecProfileLevel
|
||||||
|
* encoding profiles} for the given {@linkplain MediaCodecInfo encoder} and {@linkplain MimeTypes
|
||||||
|
* MIME type}.
|
||||||
|
*/
|
||||||
|
public static ImmutableSet<Integer> findSupportedEncodingProfiles(
|
||||||
|
MediaCodecInfo encoderInfo, String mimeType) {
|
||||||
|
MediaCodecInfo.CodecProfileLevel[] profileLevels =
|
||||||
|
encoderInfo.getCapabilitiesForType(mimeType).profileLevels;
|
||||||
|
ImmutableSet.Builder<Integer> supportedProfilesBuilder = new ImmutableSet.Builder<>();
|
||||||
|
for (MediaCodecInfo.CodecProfileLevel profileLevel : profileLevels) {
|
||||||
|
supportedProfilesBuilder.add(profileLevel.profile);
|
||||||
|
}
|
||||||
|
return supportedProfilesBuilder.build();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds the highest supported encoding level given a profile.
|
* Finds the highest supported encoding level given a profile.
|
||||||
*
|
*
|
||||||
@ -180,17 +223,10 @@ public final class EncoderUtil {
|
|||||||
return mediaCodecName;
|
return mediaCodecName;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Returns the range of supported bitrates for the given {@linkplain MimeTypes MIME type}. */
|
||||||
* Finds the {@linkplain MediaCodecInfo encoder}'s closest supported bitrate from the given
|
public static Range<Integer> getSupportedBitrateRange(
|
||||||
* bitrate.
|
MediaCodecInfo encoderInfo, String mimeType) {
|
||||||
*/
|
return encoderInfo.getCapabilitiesForType(mimeType).getVideoCapabilities().getBitrateRange();
|
||||||
public static int getClosestSupportedBitrate(
|
|
||||||
MediaCodecInfo encoderInfo, String mimeType, int bitrate) {
|
|
||||||
return encoderInfo
|
|
||||||
.getCapabilitiesForType(mimeType)
|
|
||||||
.getVideoCapabilities()
|
|
||||||
.getBitrateRange()
|
|
||||||
.clamp(bitrate);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns whether the bitrate mode is supported by the encoder. */
|
/** Returns whether the bitrate mode is supported by the encoder. */
|
||||||
@ -202,6 +238,17 @@ public final class EncoderUtil {
|
|||||||
.isBitrateModeSupported(bitrateMode);
|
.isBitrateModeSupported(bitrateMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a {@link ImmutableList list} of supported {@linkplain
|
||||||
|
* MediaCodecInfo.CodecCapabilities#colorFormats color formats} for the given {@linkplain
|
||||||
|
* MediaCodecInfo encoder} and {@linkplain MimeTypes MIME type}.
|
||||||
|
*/
|
||||||
|
public static ImmutableList<Integer> getSupportedColorFormats(
|
||||||
|
MediaCodecInfo encoderInfo, String mimeType) {
|
||||||
|
return ImmutableList.copyOf(
|
||||||
|
Ints.asList(encoderInfo.getCapabilitiesForType(mimeType).colorFormats));
|
||||||
|
}
|
||||||
|
|
||||||
/** Checks if a {@linkplain MediaCodecInfo codec} is hardware-accelerated. */
|
/** Checks if a {@linkplain MediaCodecInfo codec} is hardware-accelerated. */
|
||||||
public static boolean isHardwareAccelerated(MediaCodecInfo encoderInfo, String mimeType) {
|
public static boolean isHardwareAccelerated(MediaCodecInfo encoderInfo, String mimeType) {
|
||||||
// TODO(b/214964116): Merge into MediaCodecUtil.
|
// TODO(b/214964116): Merge into MediaCodecUtil.
|
||||||
@ -213,6 +260,18 @@ public final class EncoderUtil {
|
|||||||
return !isSoftwareOnly(encoderInfo, mimeType);
|
return !isSoftwareOnly(encoderInfo, mimeType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Returns whether a given feature is supported. */
|
||||||
|
public static boolean isFeatureSupported(
|
||||||
|
MediaCodecInfo encoderInfo, String mimeType, String featureName) {
|
||||||
|
return encoderInfo.getCapabilitiesForType(mimeType).isFeatureSupported(featureName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the number of max number of the supported concurrent codec instances. */
|
||||||
|
@RequiresApi(23)
|
||||||
|
public static int getMaxSupportedInstances(MediaCodecInfo encoderInfo, String mimeType) {
|
||||||
|
return encoderInfo.getCapabilitiesForType(mimeType).getMaxSupportedInstances();
|
||||||
|
}
|
||||||
|
|
||||||
private static boolean isSoftwareOnly(MediaCodecInfo encoderInfo, String mimeType) {
|
private static boolean isSoftwareOnly(MediaCodecInfo encoderInfo, String mimeType) {
|
||||||
if (Util.SDK_INT >= 29) {
|
if (Util.SDK_INT >= 29) {
|
||||||
return Api29.isSoftwareOnly(encoderInfo);
|
return Api29.isSoftwareOnly(encoderInfo);
|
||||||
@ -256,18 +315,25 @@ public final class EncoderUtil {
|
|||||||
: alignment * Math.round((float) size / alignment);
|
: alignment * Math.round((float) size / alignment);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static synchronized void maybePopulateEncoderInfos() {
|
private static ImmutableListMultimap<String, MediaCodecInfo> populateEncoderInfos() {
|
||||||
if (encoders.isEmpty()) {
|
ImmutableListMultimap.Builder<String, MediaCodecInfo> encoderInfosBuilder =
|
||||||
MediaCodecList mediaCodecList = new MediaCodecList(MediaCodecList.ALL_CODECS);
|
new ImmutableListMultimap.Builder<>();
|
||||||
MediaCodecInfo[] allCodecInfos = mediaCodecList.getCodecInfos();
|
|
||||||
|
|
||||||
for (MediaCodecInfo mediaCodecInfo : allCodecInfos) {
|
MediaCodecList mediaCodecList = new MediaCodecList(MediaCodecList.ALL_CODECS);
|
||||||
if (!mediaCodecInfo.isEncoder()) {
|
MediaCodecInfo[] allCodecInfos = mediaCodecList.getCodecInfos();
|
||||||
continue;
|
|
||||||
|
for (MediaCodecInfo mediaCodecInfo : allCodecInfos) {
|
||||||
|
if (!mediaCodecInfo.isEncoder()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String[] supportedMimeTypes = mediaCodecInfo.getSupportedTypes();
|
||||||
|
for (String mimeType : supportedMimeTypes) {
|
||||||
|
if (MimeTypes.isVideo(mimeType)) {
|
||||||
|
encoderInfosBuilder.put(Ascii.toLowerCase(mimeType), mediaCodecInfo);
|
||||||
}
|
}
|
||||||
encoders.add(mediaCodecInfo);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return encoderInfosBuilder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(29)
|
@RequiresApi(29)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user