mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Split method findEncoderWithClosestFormatSupport.
Add checking for bitrate mode settings. Add logging to the encoder filtering. PiperOrigin-RevId: 435662418
This commit is contained in:
parent
db0093f4c8
commit
12543a9682
2 changed files with 93 additions and 30 deletions
|
|
@ -30,6 +30,7 @@ import android.util.Size;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.media3.common.Format;
|
import androidx.media3.common.Format;
|
||||||
import androidx.media3.common.MimeTypes;
|
import androidx.media3.common.MimeTypes;
|
||||||
|
import androidx.media3.common.util.Log;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import androidx.media3.common.util.Util;
|
import androidx.media3.common.util.Util;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
|
|
@ -42,6 +43,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
public final class DefaultEncoderFactory implements Codec.EncoderFactory {
|
public final class DefaultEncoderFactory implements Codec.EncoderFactory {
|
||||||
private static final int DEFAULT_FRAME_RATE = 30;
|
private static final int DEFAULT_FRAME_RATE = 30;
|
||||||
|
private static final String TAG = "DefaultEncoderFactory";
|
||||||
|
|
||||||
private final EncoderSelector videoEncoderSelector;
|
private final EncoderSelector videoEncoderSelector;
|
||||||
private final VideoEncoderSettings requestedVideoEncoderSettings;
|
private final VideoEncoderSettings requestedVideoEncoderSettings;
|
||||||
|
|
@ -63,15 +65,20 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
|
||||||
/**
|
/**
|
||||||
* Creates a new instance.
|
* Creates a new instance.
|
||||||
*
|
*
|
||||||
|
* <p>With format fallback enabled, when the requested {@link Format} is not supported, {@code
|
||||||
|
* DefaultEncoderFactory} finds a format that is supported by the device and configures the {@link
|
||||||
|
* Codec} with it. The fallback process may change the requested {@link Format#sampleMimeType MIME
|
||||||
|
* type}, resolution, {@link Format#bitrate bitrate}, {@link Format#codecs profile/level} etc.
|
||||||
|
*
|
||||||
* <p>Values in {@code requestedVideoEncoderSettings} could be adjusted to improve encoding
|
* <p>Values in {@code requestedVideoEncoderSettings} could be adjusted to improve encoding
|
||||||
* quality and/or reduce failures. Specifically, {@link VideoEncoderSettings#profile} and {@link
|
* quality and/or reduce failures. Specifically, {@link VideoEncoderSettings#profile} and {@link
|
||||||
* VideoEncoderSettings#level} are ignored for {@link MimeTypes#VIDEO_H264}. Consider implementing
|
* VideoEncoderSettings#level} are ignored for {@link MimeTypes#VIDEO_H264}. Consider implementing
|
||||||
* {@link Codec.EncoderFactory} if such adjustments are unwanted.
|
* {@link Codec.EncoderFactory} if such adjustments are unwanted.
|
||||||
*
|
*
|
||||||
* <p>With format fallback enabled, and when the requested {@link Format} is not supported, {@code
|
* <p>{@code requestedVideoEncoderSettings} should be handled with care because there is no
|
||||||
* DefaultEncoderFactory} finds a format that is supported by the device and configures the {@link
|
* fallback support for it. For example, using incompatible {@link VideoEncoderSettings#profile}
|
||||||
* Codec} with it. The fallback process may change the requested {@link Format#sampleMimeType MIME
|
* and {@link VideoEncoderSettings#level} can cause codec configuration failure. Setting an
|
||||||
* type}, resolution, {@link Format#bitrate bitrate}, {@link Format#codecs profile/level}, etc.
|
* unsupported {@link VideoEncoderSettings#bitrateMode} may cause encoder instantiation failure.
|
||||||
*
|
*
|
||||||
* @param videoEncoderSelector The {@link EncoderSelector}.
|
* @param videoEncoderSelector The {@link EncoderSelector}.
|
||||||
* @param requestedVideoEncoderSettings The {@link VideoEncoderSettings}.
|
* @param requestedVideoEncoderSettings The {@link VideoEncoderSettings}.
|
||||||
|
|
@ -187,7 +194,7 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds a {@link MediaCodecInfo encoder} that supports the requested format most closely.
|
* Finds an {@link MediaCodecInfo encoder} that supports the requested format most closely.
|
||||||
*
|
*
|
||||||
* <p>Returns the {@link MediaCodecInfo encoder} and the supported {@link Format} in a {@link
|
* <p>Returns the {@link MediaCodecInfo encoder} and the supported {@link Format} in a {@link
|
||||||
* Pair}, or {@code null} if none is found.
|
* Pair}, or {@code null} if none is found.
|
||||||
|
|
@ -215,27 +222,13 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
|
||||||
return new VideoEncoderQueryResult(
|
return new VideoEncoderQueryResult(
|
||||||
encodersForMimeType.get(0), requestedFormat, videoEncoderSettings);
|
encodersForMimeType.get(0), requestedFormat, videoEncoderSettings);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImmutableList<MediaCodecInfo> filteredEncoders =
|
ImmutableList<MediaCodecInfo> filteredEncoders =
|
||||||
filterEncoders(
|
filterEncodersByResolution(
|
||||||
encodersForMimeType,
|
encodersForMimeType, mimeType, requestedFormat.width, requestedFormat.height);
|
||||||
/* cost= */ (encoderInfo) -> {
|
|
||||||
@Nullable
|
|
||||||
Size closestSupportedResolution =
|
|
||||||
EncoderUtil.getSupportedResolution(
|
|
||||||
encoderInfo, mimeType, requestedFormat.width, requestedFormat.height);
|
|
||||||
if (closestSupportedResolution == null) {
|
|
||||||
// Drops encoder.
|
|
||||||
return Integer.MAX_VALUE;
|
|
||||||
}
|
|
||||||
return abs(
|
|
||||||
requestedFormat.width * requestedFormat.height
|
|
||||||
- closestSupportedResolution.getWidth()
|
|
||||||
* closestSupportedResolution.getHeight());
|
|
||||||
});
|
|
||||||
if (filteredEncoders.isEmpty()) {
|
if (filteredEncoders.isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The supported resolution is the same for all remaining encoders.
|
// The supported resolution is the same for all remaining encoders.
|
||||||
Size finalResolution =
|
Size finalResolution =
|
||||||
checkNotNull(
|
checkNotNull(
|
||||||
|
|
@ -247,14 +240,13 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
|
||||||
? videoEncoderSettings.bitrate
|
? videoEncoderSettings.bitrate
|
||||||
: getSuggestedBitrate(
|
: getSuggestedBitrate(
|
||||||
finalResolution.getWidth(), finalResolution.getHeight(), requestedFormat.frameRate);
|
finalResolution.getWidth(), finalResolution.getHeight(), requestedFormat.frameRate);
|
||||||
|
filteredEncoders = filterEncodersByBitrate(filteredEncoders, mimeType, requestedBitrate);
|
||||||
|
if (filteredEncoders.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
filteredEncoders =
|
filteredEncoders =
|
||||||
filterEncoders(
|
filterEncodersByBitrateMode(filteredEncoders, mimeType, videoEncoderSettings.bitrateMode);
|
||||||
filteredEncoders,
|
|
||||||
/* cost= */ (encoderInfo) -> {
|
|
||||||
int achievableBitrate =
|
|
||||||
EncoderUtil.getClosestSupportedBitrate(encoderInfo, mimeType, requestedBitrate);
|
|
||||||
return abs(achievableBitrate - requestedBitrate);
|
|
||||||
});
|
|
||||||
if (filteredEncoders.isEmpty()) {
|
if (filteredEncoders.isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
@ -264,6 +256,7 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
|
||||||
EncoderUtil.getClosestSupportedBitrate(pickedEncoder, mimeType, requestedBitrate);
|
EncoderUtil.getClosestSupportedBitrate(pickedEncoder, mimeType, requestedBitrate);
|
||||||
VideoEncoderSettings.Builder supportedEncodingSettingBuilder =
|
VideoEncoderSettings.Builder supportedEncodingSettingBuilder =
|
||||||
videoEncoderSettings.buildUpon().setBitrate(closestSupportedBitrate);
|
videoEncoderSettings.buildUpon().setBitrate(closestSupportedBitrate);
|
||||||
|
|
||||||
if (videoEncoderSettings.profile == VideoEncoderSettings.NO_VALUE
|
if (videoEncoderSettings.profile == VideoEncoderSettings.NO_VALUE
|
||||||
|| videoEncoderSettings.level == VideoEncoderSettings.NO_VALUE
|
|| videoEncoderSettings.level == VideoEncoderSettings.NO_VALUE
|
||||||
|| videoEncoderSettings.level
|
|| videoEncoderSettings.level
|
||||||
|
|
@ -285,6 +278,52 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
|
||||||
pickedEncoder, supportedEncoderFormat, supportedEncodingSettingBuilder.build());
|
pickedEncoder, supportedEncoderFormat, supportedEncodingSettingBuilder.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Returns a list of encoders that support the requested resolution most closely. */
|
||||||
|
private static ImmutableList<MediaCodecInfo> filterEncodersByResolution(
|
||||||
|
List<MediaCodecInfo> encoders, String mimeType, int requestedWidth, int requestedHeight) {
|
||||||
|
return filterEncoders(
|
||||||
|
encoders,
|
||||||
|
/* cost= */ (encoderInfo) -> {
|
||||||
|
@Nullable
|
||||||
|
Size closestSupportedResolution =
|
||||||
|
EncoderUtil.getSupportedResolution(
|
||||||
|
encoderInfo, mimeType, requestedWidth, requestedHeight);
|
||||||
|
if (closestSupportedResolution == null) {
|
||||||
|
// Drops encoder.
|
||||||
|
return Integer.MAX_VALUE;
|
||||||
|
}
|
||||||
|
return abs(
|
||||||
|
requestedWidth * requestedHeight
|
||||||
|
- closestSupportedResolution.getWidth() * closestSupportedResolution.getHeight());
|
||||||
|
},
|
||||||
|
/* filterName= */ "resolution");
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns a list of encoders that support the requested bitrate most closely. */
|
||||||
|
private static ImmutableList<MediaCodecInfo> filterEncodersByBitrate(
|
||||||
|
List<MediaCodecInfo> encoders, String mimeType, int requestedBitrate) {
|
||||||
|
return filterEncoders(
|
||||||
|
encoders,
|
||||||
|
/* cost= */ (encoderInfo) -> {
|
||||||
|
int achievableBitrate =
|
||||||
|
EncoderUtil.getClosestSupportedBitrate(encoderInfo, mimeType, requestedBitrate);
|
||||||
|
return abs(achievableBitrate - requestedBitrate);
|
||||||
|
},
|
||||||
|
/* filterName= */ "bitrate");
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns a list of encoders that support the requested bitrate mode. */
|
||||||
|
private static ImmutableList<MediaCodecInfo> filterEncodersByBitrateMode(
|
||||||
|
List<MediaCodecInfo> encoders, String mimeType, int requestedBitrateMode) {
|
||||||
|
return filterEncoders(
|
||||||
|
encoders,
|
||||||
|
/* cost= */ (encoderInfo) ->
|
||||||
|
EncoderUtil.isBitrateModeSupported(encoderInfo, mimeType, requestedBitrateMode)
|
||||||
|
? 0
|
||||||
|
: Integer.MAX_VALUE, // Drops encoder.
|
||||||
|
/* filterName= */ "bitrate mode");
|
||||||
|
}
|
||||||
|
|
||||||
private static final class VideoEncoderQueryResult {
|
private static final class VideoEncoderQueryResult {
|
||||||
public final MediaCodecInfo encoder;
|
public final MediaCodecInfo encoder;
|
||||||
public final Format supportedFormat;
|
public final Format supportedFormat;
|
||||||
|
|
@ -376,7 +415,7 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
|
||||||
* all encoders are {@link Integer#MAX_VALUE}.
|
* all encoders are {@link Integer#MAX_VALUE}.
|
||||||
*/
|
*/
|
||||||
private static ImmutableList<MediaCodecInfo> filterEncoders(
|
private static ImmutableList<MediaCodecInfo> filterEncoders(
|
||||||
List<MediaCodecInfo> encoders, EncoderFallbackCost cost) {
|
List<MediaCodecInfo> encoders, EncoderFallbackCost cost, String filterName) {
|
||||||
List<MediaCodecInfo> filteredEncoders = new ArrayList<>(encoders.size());
|
List<MediaCodecInfo> filteredEncoders = new ArrayList<>(encoders.size());
|
||||||
|
|
||||||
int minGap = Integer.MAX_VALUE;
|
int minGap = Integer.MAX_VALUE;
|
||||||
|
|
@ -395,9 +434,24 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
|
||||||
filteredEncoders.add(encoderInfo);
|
filteredEncoders.add(encoderInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<MediaCodecInfo> removedEncoders = new ArrayList<>(encoders);
|
||||||
|
removedEncoders.removeAll(filteredEncoders);
|
||||||
|
StringBuilder stringBuilder =
|
||||||
|
new StringBuilder("Encoders removed for ").append(filterName).append(":\n");
|
||||||
|
for (int i = 0; i < removedEncoders.size(); i++) {
|
||||||
|
MediaCodecInfo encoderInfo = removedEncoders.get(i);
|
||||||
|
stringBuilder.append(Util.formatInvariant(" %s\n", encoderInfo.getName()));
|
||||||
|
}
|
||||||
|
Log.d(TAG, stringBuilder.toString());
|
||||||
|
|
||||||
return ImmutableList.copyOf(filteredEncoders);
|
return ImmutableList.copyOf(filteredEncoders);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds a {@link MimeTypes MIME type} that is supported by the encoder and in the {@code
|
||||||
|
* allowedMimeTypes}.
|
||||||
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
private static String findFallbackMimeType(
|
private static String findFallbackMimeType(
|
||||||
EncoderSelector encoderSelector, String requestedMimeType, List<String> allowedMimeTypes) {
|
EncoderSelector encoderSelector, String requestedMimeType, List<String> allowedMimeTypes) {
|
||||||
|
|
|
||||||
|
|
@ -194,6 +194,15 @@ public final class EncoderUtil {
|
||||||
.clamp(bitrate);
|
.clamp(bitrate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Returns whether the bitrate mode is supported by the encoder. */
|
||||||
|
public static boolean isBitrateModeSupported(
|
||||||
|
MediaCodecInfo encoderInfo, String mimeType, int bitrateMode) {
|
||||||
|
return encoderInfo
|
||||||
|
.getCapabilitiesForType(mimeType)
|
||||||
|
.getEncoderCapabilities()
|
||||||
|
.isBitrateModeSupported(bitrateMode);
|
||||||
|
}
|
||||||
|
|
||||||
/** Checks if a {@link MediaCodecInfo codec} is hardware-accelerated. */
|
/** Checks if a {@link 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.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue