Skip to content

Commit

Permalink
Move Encoder quality API to VideoEncoderSettings.
Browse files Browse the repository at this point in the history
Some other minor nits and adjustments to the API logic.

PiperOrigin-RevId: 459490431
  • Loading branch information
Samrobbo authored and rohitjoins committed Jul 7, 2022
1 parent cb87b74 commit 91f1777
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 88 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ public static final class Builder {
@Nullable private EncoderSelector encoderSelector;
@Nullable private VideoEncoderSettings requestedVideoEncoderSettings;
private boolean enableFallback;
private boolean automaticQualityAdjustment;

/** Creates a new {@link Builder}. */
public Builder(Context context) {
Expand All @@ -80,15 +79,9 @@ public Builder setVideoEncoderSelector(EncoderSelector encoderSelector) {
* <p>Values in {@code requestedVideoEncoderSettings} may be ignored to improve encoding quality
* and/or reduce failures.
*
* <ul>
* <li>{@link VideoEncoderSettings#bitrate} is ignored if {@link
* Builder#setAutomaticQualityAdjustment(boolean)} is enabled and {@link
* VideoEncoderSettings#bitrateMode} is VBR.
* <li>{@link VideoEncoderSettings#profile} and {@link VideoEncoderSettings#level} are ignored
* for {@link MimeTypes#VIDEO_H264}
* </ul>
*
* <p>Consider implementing {@link Codec.EncoderFactory} if such adjustments are unwanted.
* <p>{@link VideoEncoderSettings#profile} and {@link VideoEncoderSettings#level} are ignored
* for {@link MimeTypes#VIDEO_H264}. Consider implementing {@link Codec.EncoderFactory} if such
* adjustments are unwanted.
*
* <p>{@code requestedVideoEncoderSettings} should be handled with care because there is no
* fallback support for it. For example, using incompatible {@link VideoEncoderSettings#profile}
Expand Down Expand Up @@ -119,19 +112,8 @@ public Builder setEnableFallback(boolean enableFallback) {
return this;
}

/**
* Sets whether to use automatic quality adjustment.
*
* <p>With this enabled, encoders are configured to output high quality video.
*
* <p>Default value is {@code false}.
*/
public Builder setAutomaticQualityAdjustment(boolean automaticQualityAdjustment) {
this.automaticQualityAdjustment = automaticQualityAdjustment;
return this;
}

/** Creates an instance of {@link DefaultEncoderFactory}, using defaults if values are unset. */
@SuppressWarnings("deprecation")
public DefaultEncoderFactory build() {
if (encoderSelector == null) {
encoderSelector = EncoderSelector.DEFAULT;
Expand All @@ -140,19 +122,14 @@ public DefaultEncoderFactory build() {
requestedVideoEncoderSettings = VideoEncoderSettings.DEFAULT;
}
return new DefaultEncoderFactory(
context,
encoderSelector,
requestedVideoEncoderSettings,
enableFallback,
automaticQualityAdjustment);
context, encoderSelector, requestedVideoEncoderSettings, enableFallback);
}
}

private final Context context;
private final EncoderSelector videoEncoderSelector;
private final VideoEncoderSettings requestedVideoEncoderSettings;
private final boolean enableFallback;
private final boolean automaticQualityAdjustment;

/**
* @deprecated Use {@link Builder} instead.
Expand Down Expand Up @@ -182,25 +159,10 @@ public DefaultEncoderFactory(
EncoderSelector videoEncoderSelector,
VideoEncoderSettings requestedVideoEncoderSettings,
boolean enableFallback) {
this(
context,
videoEncoderSelector,
requestedVideoEncoderSettings,
enableFallback,
/* automaticQualityAdjustment= */ false);
}

private DefaultEncoderFactory(
Context context,
EncoderSelector videoEncoderSelector,
VideoEncoderSettings requestedVideoEncoderSettings,
boolean enableFallback,
boolean automaticQualityAdjustment) {
this.context = context;
this.videoEncoderSelector = videoEncoderSelector;
this.requestedVideoEncoderSettings = requestedVideoEncoderSettings;
this.enableFallback = enableFallback;
this.automaticQualityAdjustment = automaticQualityAdjustment;
}

@Override
Expand Down Expand Up @@ -276,11 +238,11 @@ public Codec createForVideoEncoding(Format format, List<String> allowedMimeTypes
mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, round(format.frameRate));

int bitrate;
if (automaticQualityAdjustment) {

if (supportedVideoEncoderSettings.enableHighQualityTargeting) {
bitrate =
new DeviceMappedEncoderBitrateProvider()
.getBitrate(
encoderInfo.getName(), format.width, format.height, round(format.frameRate));
.getBitrate(encoderInfo.getName(), format.width, format.height, format.frameRate);
} else if (supportedVideoEncoderSettings.bitrate != VideoEncoderSettings.NO_VALUE) {
bitrate = supportedVideoEncoderSettings.bitrate;
} else {
Expand Down Expand Up @@ -367,54 +329,62 @@ private static VideoEncoderQueryResult findEncoderWithClosestFormatSupport(
return null;
}

List<MediaCodecInfo> encodersForMimeType = encoderSelector.selectEncoderInfos(mimeType);
if (encodersForMimeType.isEmpty()) {
ImmutableList<MediaCodecInfo> filteredEncoderInfos =
encoderSelector.selectEncoderInfos(mimeType);
if (filteredEncoderInfos.isEmpty()) {
return null;
}

if (!enableFallback) {
return new VideoEncoderQueryResult(
encodersForMimeType.get(0), requestedFormat, videoEncoderSettings);
filteredEncoderInfos.get(0), requestedFormat, videoEncoderSettings);
}

ImmutableList<MediaCodecInfo> filteredEncoders =
filteredEncoderInfos =
filterEncodersByResolution(
encodersForMimeType, mimeType, requestedFormat.width, requestedFormat.height);
if (filteredEncoders.isEmpty()) {
filteredEncoderInfos, mimeType, requestedFormat.width, requestedFormat.height);
if (filteredEncoderInfos.isEmpty()) {
return null;
}
// The supported resolution is the same for all remaining encoders.
Size finalResolution =
checkNotNull(
EncoderUtil.getSupportedResolution(
filteredEncoders.get(0), mimeType, requestedFormat.width, requestedFormat.height));
filteredEncoderInfos.get(0),
mimeType,
requestedFormat.width,
requestedFormat.height));

int requestedBitrate =
videoEncoderSettings.bitrate != VideoEncoderSettings.NO_VALUE
? videoEncoderSettings.bitrate
: getSuggestedBitrate(
finalResolution.getWidth(), finalResolution.getHeight(), requestedFormat.frameRate);
filteredEncoders = filterEncodersByBitrate(filteredEncoders, mimeType, requestedBitrate);
if (filteredEncoders.isEmpty()) {

filteredEncoderInfos =
filterEncodersByBitrate(filteredEncoderInfos, mimeType, requestedBitrate);
if (filteredEncoderInfos.isEmpty()) {
return null;
}

filteredEncoders =
filterEncodersByBitrateMode(filteredEncoders, mimeType, videoEncoderSettings.bitrateMode);
if (filteredEncoders.isEmpty()) {
filteredEncoderInfos =
filterEncodersByBitrateMode(
filteredEncoderInfos, mimeType, videoEncoderSettings.bitrateMode);
if (filteredEncoderInfos.isEmpty()) {
return null;
}

MediaCodecInfo pickedEncoder = filteredEncoders.get(0);
MediaCodecInfo pickedEncoderInfo = filteredEncoderInfos.get(0);
int closestSupportedBitrate =
EncoderUtil.getSupportedBitrateRange(pickedEncoder, mimeType).clamp(requestedBitrate);
EncoderUtil.getSupportedBitrateRange(pickedEncoderInfo, mimeType).clamp(requestedBitrate);
VideoEncoderSettings.Builder supportedEncodingSettingBuilder =
videoEncoderSettings.buildUpon().setBitrate(closestSupportedBitrate);

if (videoEncoderSettings.profile == VideoEncoderSettings.NO_VALUE
|| videoEncoderSettings.level == VideoEncoderSettings.NO_VALUE
|| videoEncoderSettings.level
> EncoderUtil.findHighestSupportedEncodingLevel(
pickedEncoder, mimeType, videoEncoderSettings.profile)) {
pickedEncoderInfo, mimeType, videoEncoderSettings.profile)) {
supportedEncodingSettingBuilder.setEncodingProfileLevel(
VideoEncoderSettings.NO_VALUE, VideoEncoderSettings.NO_VALUE);
}
Expand All @@ -428,7 +398,7 @@ private static VideoEncoderQueryResult findEncoderWithClosestFormatSupport(
.setAverageBitrate(closestSupportedBitrate)
.build();
return new VideoEncoderQueryResult(
pickedEncoder, supportedEncoderFormat, supportedEncodingSettingBuilder.build());
pickedEncoderInfo, supportedEncoderFormat, supportedEncodingSettingBuilder.build());
}

/** Returns a list of encoders that support the requested resolution most closely. */
Expand Down Expand Up @@ -642,7 +612,7 @@ private static boolean mimeTypeIsSupported(
* </ul>
*/
private static int getSuggestedBitrate(int width, int height, float frameRate) {
// TODO(b/210591626) Implement bitrate estimation.
// TODO(b/210591626) Refactor into a BitrateProvider.
// Assume medium motion factor.
// 1080p60 -> 16.6Mbps, 720p30 -> 3.7Mbps.
return (int) (width * height * frameRate * 0.07 * 2);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,14 @@
public class DeviceMappedEncoderBitrateProvider implements EncoderBitrateProvider {

@Override
public int getBitrate(String encoderName, int width, int height, int frameRate) {
public int getBitrate(String encoderName, int width, int height, float frameRate) {
double bitrateMultiplier =
getBitrateMultiplierFromMapping(
encoderName, Util.SDK_INT, Build.MODEL, "" + width + "x" + height, frameRate);
encoderName,
Util.SDK_INT,
Build.MODEL,
"" + width + "x" + height,
Math.round(frameRate));
return (int) (width * height * frameRate * bitrateMultiplier);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ public interface EncoderBitrateProvider {
/**
* Returns a recommended bitrate that the encoder should target.
*
* @param encoderName The name of the encoder, see {@link MediaCodecInfo#getName()}
* @param encoderName The name of the encoder, see {@link MediaCodecInfo#getName()}.
* @param width The output width of the video after encoding.
* @param height The output height of the video after encoding.
* @param frameRate The expected output frame rate of the video after encoding.
* @return The bitrate the encoder should target.
*/
int getBitrate(String encoderName, int width, int height, int frameRate);
int getBitrate(String encoderName, int width, int height, float frameRate);
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import android.media.MediaCodecInfo;
import androidx.media3.common.MimeTypes;
import androidx.media3.common.util.UnstableApi;
import java.util.List;
import com.google.common.collect.ImmutableList;

/** Selector of {@link MediaCodec} encoder instances. */
@UnstableApi
Expand All @@ -37,8 +37,8 @@ public interface EncoderSelector {
* order.
*
* @param mimeType The {@linkplain MimeTypes MIME type} for which an encoder is required.
* @return An unmodifiable list of {@linkplain MediaCodecInfo encoders} that support the {@code
* @return An immutable list of {@linkplain MediaCodecInfo encoders} that support the {@code
* mimeType}. The list may be empty.
*/
List<MediaCodecInfo> selectEncoderInfos(String mimeType);
ImmutableList<MediaCodecInfo> selectEncoderInfos(String mimeType);
}
Loading

0 comments on commit 91f1777

Please sign in to comment.