mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Add encoder setting to set frame repeat interval
This can be used for screen recording to produce files without large gaps between frames. PiperOrigin-RevId: 726451010
This commit is contained in:
parent
2a91d47ea9
commit
04d9a751c6
@ -385,6 +385,13 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
long repeatPreviousFrameIntervalUs =
|
||||||
|
supportedVideoEncoderSettings.repeatPreviousFrameIntervalUs;
|
||||||
|
if (repeatPreviousFrameIntervalUs != VideoEncoderSettings.NO_VALUE) {
|
||||||
|
mediaFormat.setLong(
|
||||||
|
MediaFormat.KEY_REPEAT_PREVIOUS_FRAME_AFTER, repeatPreviousFrameIntervalUs);
|
||||||
|
}
|
||||||
|
|
||||||
if (Util.SDK_INT >= 35) {
|
if (Util.SDK_INT >= 35) {
|
||||||
mediaFormat.setInteger(MediaFormat.KEY_IMPORTANCE, max(0, -codecPriority));
|
mediaFormat.setInteger(MediaFormat.KEY_IMPORTANCE, max(0, -codecPriority));
|
||||||
}
|
}
|
||||||
|
@ -82,6 +82,7 @@ public final class VideoEncoderSettings {
|
|||||||
private float iFrameIntervalSeconds;
|
private float iFrameIntervalSeconds;
|
||||||
private int operatingRate;
|
private int operatingRate;
|
||||||
private int priority;
|
private int priority;
|
||||||
|
private long repeatPreviousFrameIntervalUs;
|
||||||
private boolean enableHighQualityTargeting;
|
private boolean enableHighQualityTargeting;
|
||||||
|
|
||||||
/** Creates a new instance. */
|
/** Creates a new instance. */
|
||||||
@ -93,6 +94,7 @@ public final class VideoEncoderSettings {
|
|||||||
this.iFrameIntervalSeconds = DEFAULT_I_FRAME_INTERVAL_SECONDS;
|
this.iFrameIntervalSeconds = DEFAULT_I_FRAME_INTERVAL_SECONDS;
|
||||||
this.operatingRate = NO_VALUE;
|
this.operatingRate = NO_VALUE;
|
||||||
this.priority = NO_VALUE;
|
this.priority = NO_VALUE;
|
||||||
|
this.repeatPreviousFrameIntervalUs = NO_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Builder(VideoEncoderSettings videoEncoderSettings) {
|
private Builder(VideoEncoderSettings videoEncoderSettings) {
|
||||||
@ -103,6 +105,7 @@ public final class VideoEncoderSettings {
|
|||||||
this.iFrameIntervalSeconds = videoEncoderSettings.iFrameIntervalSeconds;
|
this.iFrameIntervalSeconds = videoEncoderSettings.iFrameIntervalSeconds;
|
||||||
this.operatingRate = videoEncoderSettings.operatingRate;
|
this.operatingRate = videoEncoderSettings.operatingRate;
|
||||||
this.priority = videoEncoderSettings.priority;
|
this.priority = videoEncoderSettings.priority;
|
||||||
|
this.repeatPreviousFrameIntervalUs = videoEncoderSettings.repeatPreviousFrameIntervalUs;
|
||||||
this.enableHighQualityTargeting = videoEncoderSettings.enableHighQualityTargeting;
|
this.enableHighQualityTargeting = videoEncoderSettings.enableHighQualityTargeting;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -190,6 +193,21 @@ public final class VideoEncoderSettings {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the threshold duration between input frames beyond which to repeat the previous frame if
|
||||||
|
* no new frame has been received, in microseconds. The default value is {@link #NO_VALUE},
|
||||||
|
* which means that frames are not automatically repeated.
|
||||||
|
*
|
||||||
|
* @param repeatPreviousFrameIntervalUs The {@linkplain
|
||||||
|
* MediaFormat#KEY_REPEAT_PREVIOUS_FRAME_AFTER frame repeat interval}, in microseconds.
|
||||||
|
* @return This builder.
|
||||||
|
*/
|
||||||
|
@CanIgnoreReturnValue
|
||||||
|
public Builder setRepeatPreviousFrameIntervalUs(long repeatPreviousFrameIntervalUs) {
|
||||||
|
this.repeatPreviousFrameIntervalUs = repeatPreviousFrameIntervalUs;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets whether to enable automatic adjustment of the bitrate to target a high quality encoding.
|
* Sets whether to enable automatic adjustment of the bitrate to target a high quality encoding.
|
||||||
*
|
*
|
||||||
@ -223,6 +241,7 @@ public final class VideoEncoderSettings {
|
|||||||
iFrameIntervalSeconds,
|
iFrameIntervalSeconds,
|
||||||
operatingRate,
|
operatingRate,
|
||||||
priority,
|
priority,
|
||||||
|
repeatPreviousFrameIntervalUs,
|
||||||
enableHighQualityTargeting);
|
enableHighQualityTargeting);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -248,6 +267,12 @@ public final class VideoEncoderSettings {
|
|||||||
/** The encoder {@link MediaFormat#KEY_PRIORITY priority}. */
|
/** The encoder {@link MediaFormat#KEY_PRIORITY priority}. */
|
||||||
public final int priority;
|
public final int priority;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@linkplain MediaFormat#KEY_REPEAT_PREVIOUS_FRAME_AFTER frame repeat interval}, in
|
||||||
|
* microseconds.
|
||||||
|
*/
|
||||||
|
public final long repeatPreviousFrameIntervalUs;
|
||||||
|
|
||||||
/** Whether the encoder should automatically set the bitrate to target a high quality encoding. */
|
/** Whether the encoder should automatically set the bitrate to target a high quality encoding. */
|
||||||
public final boolean enableHighQualityTargeting;
|
public final boolean enableHighQualityTargeting;
|
||||||
|
|
||||||
@ -259,6 +284,7 @@ public final class VideoEncoderSettings {
|
|||||||
float iFrameIntervalSeconds,
|
float iFrameIntervalSeconds,
|
||||||
int operatingRate,
|
int operatingRate,
|
||||||
int priority,
|
int priority,
|
||||||
|
long repeatPreviousFrameIntervalUs,
|
||||||
boolean enableHighQualityTargeting) {
|
boolean enableHighQualityTargeting) {
|
||||||
this.bitrate = bitrate;
|
this.bitrate = bitrate;
|
||||||
this.bitrateMode = bitrateMode;
|
this.bitrateMode = bitrateMode;
|
||||||
@ -267,6 +293,7 @@ public final class VideoEncoderSettings {
|
|||||||
this.iFrameIntervalSeconds = iFrameIntervalSeconds;
|
this.iFrameIntervalSeconds = iFrameIntervalSeconds;
|
||||||
this.operatingRate = operatingRate;
|
this.operatingRate = operatingRate;
|
||||||
this.priority = priority;
|
this.priority = priority;
|
||||||
|
this.repeatPreviousFrameIntervalUs = repeatPreviousFrameIntervalUs;
|
||||||
this.enableHighQualityTargeting = enableHighQualityTargeting;
|
this.enableHighQualityTargeting = enableHighQualityTargeting;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -293,6 +320,7 @@ public final class VideoEncoderSettings {
|
|||||||
&& iFrameIntervalSeconds == that.iFrameIntervalSeconds
|
&& iFrameIntervalSeconds == that.iFrameIntervalSeconds
|
||||||
&& operatingRate == that.operatingRate
|
&& operatingRate == that.operatingRate
|
||||||
&& priority == that.priority
|
&& priority == that.priority
|
||||||
|
&& repeatPreviousFrameIntervalUs == that.repeatPreviousFrameIntervalUs
|
||||||
&& enableHighQualityTargeting == that.enableHighQualityTargeting;
|
&& enableHighQualityTargeting == that.enableHighQualityTargeting;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -306,6 +334,9 @@ public final class VideoEncoderSettings {
|
|||||||
result = 31 * result + Float.floatToIntBits(iFrameIntervalSeconds);
|
result = 31 * result + Float.floatToIntBits(iFrameIntervalSeconds);
|
||||||
result = 31 * result + operatingRate;
|
result = 31 * result + operatingRate;
|
||||||
result = 31 * result + priority;
|
result = 31 * result + priority;
|
||||||
|
result =
|
||||||
|
31 * result
|
||||||
|
+ (int) (repeatPreviousFrameIntervalUs ^ (repeatPreviousFrameIntervalUs >>> 32));
|
||||||
result = 31 * result + (enableHighQualityTargeting ? 1 : 0);
|
result = 31 * result + (enableHighQualityTargeting ? 1 : 0);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -323,6 +323,42 @@ public class DefaultEncoderFactoryTest {
|
|||||||
assertThat(configurationMediaFormat.containsKey(MediaFormat.KEY_OPERATING_RATE)).isFalse();
|
assertThat(configurationMediaFormat.containsKey(MediaFormat.KEY_OPERATING_RATE)).isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void
|
||||||
|
createForVideoEncoding_withRepeatPreviousFrameIntervalUs_configuresEncoderWithRepeatPreviousFrameIntervalUs()
|
||||||
|
throws Exception {
|
||||||
|
Format requestedVideoFormat = createVideoFormat(MimeTypes.VIDEO_H264, 1920, 1080, 30);
|
||||||
|
DefaultCodec videoEncoder =
|
||||||
|
new DefaultEncoderFactory.Builder(context)
|
||||||
|
.setRequestedVideoEncoderSettings(
|
||||||
|
new VideoEncoderSettings.Builder().setRepeatPreviousFrameIntervalUs(33_333).build())
|
||||||
|
.build()
|
||||||
|
.createForVideoEncoding(requestedVideoFormat);
|
||||||
|
|
||||||
|
assertThat(
|
||||||
|
videoEncoder
|
||||||
|
.getConfigurationMediaFormat()
|
||||||
|
.getLong(MediaFormat.KEY_REPEAT_PREVIOUS_FRAME_AFTER))
|
||||||
|
.isEqualTo(33_333);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void
|
||||||
|
createForVideoEncoding_withDefaultEncoderSettings_doesNotConfigureRepeatPreviousFrameIntervalUs()
|
||||||
|
throws Exception {
|
||||||
|
Format requestedVideoFormat = createVideoFormat(MimeTypes.VIDEO_H264, 1920, 1080, 30);
|
||||||
|
DefaultCodec videoEncoder =
|
||||||
|
new DefaultEncoderFactory.Builder(context)
|
||||||
|
.build()
|
||||||
|
.createForVideoEncoding(requestedVideoFormat);
|
||||||
|
|
||||||
|
assertThat(
|
||||||
|
videoEncoder
|
||||||
|
.getConfigurationMediaFormat()
|
||||||
|
.containsKey(MediaFormat.KEY_REPEAT_PREVIOUS_FRAME_AFTER))
|
||||||
|
.isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void createForVideoEncoding_withNoAvailableEncoderFromEncoderSelector_throws() {
|
public void createForVideoEncoding_withNoAvailableEncoderFromEncoderSelector_throws() {
|
||||||
Format requestedVideoFormat = createVideoFormat(MimeTypes.VIDEO_H264, 1920, 1080, 30);
|
Format requestedVideoFormat = createVideoFormat(MimeTypes.VIDEO_H264, 1920, 1080, 30);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user