mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Fix VideoEncoderSetting.bitrate is ignored when set
PiperOrigin-RevId: 491377695
This commit is contained in:
parent
3581ccde29
commit
0f85ce5dbf
@ -206,6 +206,25 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
|
|||||||
/* outputSurface= */ null);
|
/* outputSurface= */ null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a {@link DefaultCodec} for video encoding.
|
||||||
|
*
|
||||||
|
* <p>Use {@link Builder#setRequestedVideoEncoderSettings} with {@link
|
||||||
|
* VideoEncoderSettings#bitrate} set to request for a specific encoding bitrate. Bitrate settings
|
||||||
|
* in {@link Format} are ignored when {@link VideoEncoderSettings#bitrate} or {@link
|
||||||
|
* VideoEncoderSettings#enableHighQualityTargeting} is set.
|
||||||
|
*
|
||||||
|
* @param format The {@link Format} (of the output data) used to determine the underlying encoder
|
||||||
|
* and its configuration values. {@link Format#sampleMimeType}, {@link Format#width} and
|
||||||
|
* {@link Format#height} are set to those of the desired output video format. {@link
|
||||||
|
* Format#rotationDegrees} is 0 and {@link Format#width} {@code >=} {@link Format#height},
|
||||||
|
* therefore the video is always in landscape orientation. {@link Format#frameRate} is set to
|
||||||
|
* the output video's frame rate, if available.
|
||||||
|
* @param allowedMimeTypes The non-empty list of allowed output sample {@linkplain MimeTypes MIME
|
||||||
|
* types}.
|
||||||
|
* @return A {@link DefaultCodec} for video encoding.
|
||||||
|
* @throws TransformationException If no suitable {@link DefaultCodec} can be created.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Codec createForVideoEncoding(Format format, List<String> allowedMimeTypes)
|
public Codec createForVideoEncoding(Format format, List<String> allowedMimeTypes)
|
||||||
throws TransformationException {
|
throws TransformationException {
|
||||||
@ -246,25 +265,34 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
|
|||||||
mimeType, encoderSupportedFormat.width, encoderSupportedFormat.height);
|
mimeType, encoderSupportedFormat.width, encoderSupportedFormat.height);
|
||||||
mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, round(encoderSupportedFormat.frameRate));
|
mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, round(encoderSupportedFormat.frameRate));
|
||||||
|
|
||||||
if (supportedVideoEncoderSettings.enableHighQualityTargeting) {
|
int finalBitrate;
|
||||||
int bitrate =
|
if (enableFallback) {
|
||||||
|
finalBitrate = supportedVideoEncoderSettings.bitrate;
|
||||||
|
} else {
|
||||||
|
// supportedVideoEncoderSettings is identical to requestedVideoEncoderSettings.
|
||||||
|
if (supportedVideoEncoderSettings.bitrate != VideoEncoderSettings.NO_VALUE) {
|
||||||
|
finalBitrate = supportedVideoEncoderSettings.bitrate;
|
||||||
|
} else if (supportedVideoEncoderSettings.enableHighQualityTargeting) {
|
||||||
|
finalBitrate =
|
||||||
new DeviceMappedEncoderBitrateProvider()
|
new DeviceMappedEncoderBitrateProvider()
|
||||||
.getBitrate(
|
.getBitrate(
|
||||||
encoderInfo.getName(),
|
encoderInfo.getName(),
|
||||||
encoderSupportedFormat.width,
|
encoderSupportedFormat.width,
|
||||||
encoderSupportedFormat.height,
|
encoderSupportedFormat.height,
|
||||||
encoderSupportedFormat.frameRate);
|
encoderSupportedFormat.frameRate);
|
||||||
encoderSupportedFormat =
|
} else if (encoderSupportedFormat.averageBitrate != Format.NO_VALUE) {
|
||||||
encoderSupportedFormat.buildUpon().setAverageBitrate(bitrate).build();
|
finalBitrate = encoderSupportedFormat.averageBitrate;
|
||||||
} else if (encoderSupportedFormat.bitrate == Format.NO_VALUE) {
|
} else {
|
||||||
int bitrate =
|
finalBitrate =
|
||||||
getSuggestedBitrate(
|
getSuggestedBitrate(
|
||||||
encoderSupportedFormat.width,
|
encoderSupportedFormat.width,
|
||||||
encoderSupportedFormat.height,
|
encoderSupportedFormat.height,
|
||||||
encoderSupportedFormat.frameRate);
|
encoderSupportedFormat.frameRate);
|
||||||
encoderSupportedFormat =
|
|
||||||
encoderSupportedFormat.buildUpon().setAverageBitrate(bitrate).build();
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
encoderSupportedFormat =
|
||||||
|
encoderSupportedFormat.buildUpon().setAverageBitrate(finalBitrate).build();
|
||||||
|
|
||||||
mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, encoderSupportedFormat.averageBitrate);
|
mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, encoderSupportedFormat.averageBitrate);
|
||||||
mediaFormat.setInteger(MediaFormat.KEY_BITRATE_MODE, supportedVideoEncoderSettings.bitrateMode);
|
mediaFormat.setInteger(MediaFormat.KEY_BITRATE_MODE, supportedVideoEncoderSettings.bitrateMode);
|
||||||
@ -389,17 +417,24 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
|
|||||||
requestedFormat.width,
|
requestedFormat.width,
|
||||||
requestedFormat.height));
|
requestedFormat.height));
|
||||||
|
|
||||||
int requestedBitrate =
|
int requestedBitrate = Format.NO_VALUE;
|
||||||
|
// Encoders are not filtered by bitrate if high quality targeting is enabled.
|
||||||
|
if (!videoEncoderSettings.enableHighQualityTargeting) {
|
||||||
|
requestedBitrate =
|
||||||
videoEncoderSettings.bitrate != VideoEncoderSettings.NO_VALUE
|
videoEncoderSettings.bitrate != VideoEncoderSettings.NO_VALUE
|
||||||
? videoEncoderSettings.bitrate
|
? videoEncoderSettings.bitrate
|
||||||
|
: requestedFormat.averageBitrate != Format.NO_VALUE
|
||||||
|
? requestedFormat.averageBitrate
|
||||||
: getSuggestedBitrate(
|
: getSuggestedBitrate(
|
||||||
finalResolution.getWidth(), finalResolution.getHeight(), requestedFormat.frameRate);
|
finalResolution.getWidth(),
|
||||||
|
finalResolution.getHeight(),
|
||||||
|
requestedFormat.frameRate);
|
||||||
filteredEncoderInfos =
|
filteredEncoderInfos =
|
||||||
filterEncodersByBitrate(filteredEncoderInfos, mimeType, requestedBitrate);
|
filterEncodersByBitrate(filteredEncoderInfos, mimeType, requestedBitrate);
|
||||||
if (filteredEncoderInfos.isEmpty()) {
|
if (filteredEncoderInfos.isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
filteredEncoderInfos =
|
filteredEncoderInfos =
|
||||||
filterEncodersByBitrateMode(
|
filterEncodersByBitrateMode(
|
||||||
@ -408,11 +443,6 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(b/238094555): Check encoder supports bitrate targeted by high quality.
|
|
||||||
MediaCodecInfo pickedEncoderInfo = filteredEncoderInfos.get(0);
|
|
||||||
int closestSupportedBitrate =
|
|
||||||
EncoderUtil.getSupportedBitrateRange(pickedEncoderInfo, mimeType).clamp(requestedBitrate);
|
|
||||||
|
|
||||||
VideoEncoderSettings.Builder supportedEncodingSettingBuilder = videoEncoderSettings.buildUpon();
|
VideoEncoderSettings.Builder supportedEncodingSettingBuilder = videoEncoderSettings.buildUpon();
|
||||||
Format.Builder encoderSupportedFormatBuilder =
|
Format.Builder encoderSupportedFormatBuilder =
|
||||||
requestedFormat
|
requestedFormat
|
||||||
@ -420,11 +450,23 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
|
|||||||
.setSampleMimeType(mimeType)
|
.setSampleMimeType(mimeType)
|
||||||
.setWidth(finalResolution.getWidth())
|
.setWidth(finalResolution.getWidth())
|
||||||
.setHeight(finalResolution.getHeight());
|
.setHeight(finalResolution.getHeight());
|
||||||
|
MediaCodecInfo pickedEncoderInfo = filteredEncoderInfos.get(0);
|
||||||
if (!videoEncoderSettings.enableHighQualityTargeting) {
|
if (videoEncoderSettings.enableHighQualityTargeting) {
|
||||||
|
requestedBitrate =
|
||||||
|
new DeviceMappedEncoderBitrateProvider()
|
||||||
|
.getBitrate(
|
||||||
|
pickedEncoderInfo.getName(),
|
||||||
|
finalResolution.getWidth(),
|
||||||
|
finalResolution.getHeight(),
|
||||||
|
requestedFormat.frameRate);
|
||||||
|
// Resets the flag after getting a targeted bitrate, so that supportedEncodingSetting can have
|
||||||
|
// bitrate set.
|
||||||
|
supportedEncodingSettingBuilder.setEnableHighQualityTargeting(false);
|
||||||
|
}
|
||||||
|
int closestSupportedBitrate =
|
||||||
|
EncoderUtil.getSupportedBitrateRange(pickedEncoderInfo, mimeType).clamp(requestedBitrate);
|
||||||
supportedEncodingSettingBuilder.setBitrate(closestSupportedBitrate);
|
supportedEncodingSettingBuilder.setBitrate(closestSupportedBitrate);
|
||||||
encoderSupportedFormatBuilder.setAverageBitrate(closestSupportedBitrate);
|
encoderSupportedFormatBuilder.setAverageBitrate(closestSupportedBitrate);
|
||||||
}
|
|
||||||
|
|
||||||
if (videoEncoderSettings.profile == VideoEncoderSettings.NO_VALUE
|
if (videoEncoderSettings.profile == VideoEncoderSettings.NO_VALUE
|
||||||
|| videoEncoderSettings.level == VideoEncoderSettings.NO_VALUE
|
|| videoEncoderSettings.level == VideoEncoderSettings.NO_VALUE
|
||||||
|
@ -131,6 +131,96 @@ public class DefaultEncoderFactoryTest {
|
|||||||
assertThat(actualVideoFormat.height).isEqualTo(1080);
|
assertThat(actualVideoFormat.height).isEqualTo(1080);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void
|
||||||
|
createForVideoEncoding_setFormatAverageBitrateUnsetVideoEncoderSettings_configuresEncoderUsingFormatAverageBitrate()
|
||||||
|
throws Exception {
|
||||||
|
Format requestedVideoFormat = createVideoFormat(MimeTypes.VIDEO_H264, 1920, 1080, 30);
|
||||||
|
requestedVideoFormat = requestedVideoFormat.buildUpon().setAverageBitrate(5_000_000).build();
|
||||||
|
|
||||||
|
Format actualVideoFormat =
|
||||||
|
new DefaultEncoderFactory.Builder(context)
|
||||||
|
.setRequestedVideoEncoderSettings(VideoEncoderSettings.DEFAULT)
|
||||||
|
.build()
|
||||||
|
.createForVideoEncoding(
|
||||||
|
requestedVideoFormat,
|
||||||
|
/* allowedMimeTypes= */ ImmutableList.of(MimeTypes.VIDEO_H264))
|
||||||
|
.getConfigurationFormat();
|
||||||
|
|
||||||
|
assertThat(actualVideoFormat.sampleMimeType).isEqualTo(MimeTypes.VIDEO_H264);
|
||||||
|
assertThat(actualVideoFormat.width).isEqualTo(1920);
|
||||||
|
assertThat(actualVideoFormat.height).isEqualTo(1080);
|
||||||
|
assertThat(actualVideoFormat.averageBitrate).isEqualTo(5_000_000);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void
|
||||||
|
createForVideoEncoding_unsetFormatAverageBitrateAndUnsetVideoEncoderSettingsBitrate_configuresEncoderUsingDefaultBitrateMapping()
|
||||||
|
throws Exception {
|
||||||
|
Format requestedVideoFormat = createVideoFormat(MimeTypes.VIDEO_H264, 1920, 1080, 30);
|
||||||
|
Format actualVideoFormat =
|
||||||
|
new DefaultEncoderFactory.Builder(context)
|
||||||
|
.build()
|
||||||
|
.createForVideoEncoding(
|
||||||
|
requestedVideoFormat,
|
||||||
|
/* allowedMimeTypes= */ ImmutableList.of(MimeTypes.VIDEO_H264))
|
||||||
|
.getConfigurationFormat();
|
||||||
|
|
||||||
|
assertThat(actualVideoFormat.sampleMimeType).isEqualTo(MimeTypes.VIDEO_H264);
|
||||||
|
assertThat(actualVideoFormat.width).isEqualTo(1920);
|
||||||
|
assertThat(actualVideoFormat.height).isEqualTo(1080);
|
||||||
|
// The default behavior is to use DefaultEncoderFactory#getSuggestedBitrate.
|
||||||
|
// 1920 * 1080 * 30 * 0.07 * 2.
|
||||||
|
assertThat(actualVideoFormat.averageBitrate).isEqualTo(8_709_120);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void
|
||||||
|
createForVideoEncoding_setFormatAverageBitrateAndSetVideoEncoderSettingHighQualityTargeting_configuresEncoderUsingHighQualityTargeting()
|
||||||
|
throws Exception {
|
||||||
|
Format requestedVideoFormat = createVideoFormat(MimeTypes.VIDEO_H264, 1920, 1080, 30);
|
||||||
|
requestedVideoFormat = requestedVideoFormat.buildUpon().setAverageBitrate(5_000_000).build();
|
||||||
|
Format actualVideoFormat =
|
||||||
|
new DefaultEncoderFactory.Builder(context)
|
||||||
|
.setRequestedVideoEncoderSettings(
|
||||||
|
new VideoEncoderSettings.Builder().setEnableHighQualityTargeting(true).build())
|
||||||
|
.build()
|
||||||
|
.createForVideoEncoding(
|
||||||
|
requestedVideoFormat,
|
||||||
|
/* allowedMimeTypes= */ ImmutableList.of(MimeTypes.VIDEO_H264))
|
||||||
|
.getConfigurationFormat();
|
||||||
|
|
||||||
|
assertThat(actualVideoFormat.sampleMimeType).isEqualTo(MimeTypes.VIDEO_H264);
|
||||||
|
assertThat(actualVideoFormat.width).isEqualTo(1920);
|
||||||
|
assertThat(actualVideoFormat.height).isEqualTo(1080);
|
||||||
|
// DeviceMappedEncoderBitrateProvider will produce 1920 * 1080 * 30 * 1.4, but the value is
|
||||||
|
// clampped down to the encoder's maximum, 25_000_000.
|
||||||
|
assertThat(actualVideoFormat.averageBitrate).isEqualTo(25_000_000);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void
|
||||||
|
createForVideoEncoding_setFormatAverageBitrateAndVideoEncoderSettingsBitrate_configuresEncoderUsingVideoEncoderSettingsBitrate()
|
||||||
|
throws Exception {
|
||||||
|
Format requestedVideoFormat = createVideoFormat(MimeTypes.VIDEO_H264, 1920, 1080, 30);
|
||||||
|
requestedVideoFormat = requestedVideoFormat.buildUpon().setAverageBitrate(5_000_000).build();
|
||||||
|
|
||||||
|
Format actualVideoFormat =
|
||||||
|
new DefaultEncoderFactory.Builder(context)
|
||||||
|
.setRequestedVideoEncoderSettings(
|
||||||
|
new VideoEncoderSettings.Builder().setBitrate(10_000_000).build())
|
||||||
|
.build()
|
||||||
|
.createForVideoEncoding(
|
||||||
|
requestedVideoFormat,
|
||||||
|
/* allowedMimeTypes= */ ImmutableList.of(MimeTypes.VIDEO_H264))
|
||||||
|
.getConfigurationFormat();
|
||||||
|
|
||||||
|
assertThat(actualVideoFormat.sampleMimeType).isEqualTo(MimeTypes.VIDEO_H264);
|
||||||
|
assertThat(actualVideoFormat.width).isEqualTo(1920);
|
||||||
|
assertThat(actualVideoFormat.height).isEqualTo(1080);
|
||||||
|
assertThat(actualVideoFormat.averageBitrate).isEqualTo(10_000_000);
|
||||||
|
}
|
||||||
|
|
||||||
@Config(sdk = 29)
|
@Config(sdk = 29)
|
||||||
@Test
|
@Test
|
||||||
public void
|
public void
|
||||||
|
Loading…
x
Reference in New Issue
Block a user