Calculate video encoding settings based on trimmed duration.

This commit is contained in:
Nicholas Tinsley
2024-01-25 15:21:00 -05:00
committed by Greyson Parrelli
parent a3438d3345
commit 6fc9055221
7 changed files with 33 additions and 21 deletions
@@ -249,10 +249,12 @@ public final class AttachmentCompressionJob extends BaseJob {
throw new UndeliverableMessageException("Cannot get media data source for attachment.");
}
allowSkipOnFailure = !transformProperties.getVideoEdited();
TranscoderOptions options = null;
if (transformProperties.videoTrim) {
options = new TranscoderOptions(transformProperties.videoTrimStartTimeUs, transformProperties.videoTrimEndTimeUs);
if (transformProperties != null) {
allowSkipOnFailure = !transformProperties.getVideoEdited();
if (transformProperties.videoTrim) {
options = new TranscoderOptions(transformProperties.videoTrimStartTimeUs, transformProperties.videoTrimEndTimeUs);
}
}
if (FeatureFlags.useStreamingVideoMuxer()) {
@@ -98,7 +98,7 @@ public final class VideoEditorHud extends LinearLayout {
long size = tryGetUriSize(getContext(), uri, Long.MAX_VALUE);
if (size > maxSendSize) {
videoTimeLine.setTimeLimit(VideoUtil.getMaxVideoUploadDurationInSeconds(), TimeUnit.SECONDS);
videoTimeLine.setTimeLimit(videoBitRateCalculator.getMaxVideoUploadDurationInSeconds(), TimeUnit.SECONDS);
}
videoTimeLine.setOnRangeChangeListener(new VideoThumbnailsRangeSelectorView.OnRangeChangeListener() {
@@ -27,6 +27,7 @@ import java.io.FileInputStream;
import java.io.IOException;
import java.text.NumberFormat;
import java.util.Locale;
import java.util.concurrent.TimeUnit;
@RequiresApi(26)
public final class InMemoryTranscoder implements Closeable {
@@ -63,8 +64,13 @@ public final class InMemoryTranscoder implements Closeable {
throw new VideoSourceException("Unable to read datasource", e);
}
if (options != null && options.endTimeUs != 0) {
this.duration = TimeUnit.MICROSECONDS.toMillis(options.endTimeUs - options.startTimeUs);
} else {
this.duration = getDuration(mediaMetadataRetriever);
}
this.inSize = dataSource.getSize();
this.duration = getDuration(mediaMetadataRetriever);
this.inputBitRate = VideoBitRateCalculator.bitRate(inSize, duration);
this.targetQuality = new VideoBitRateCalculator(upperSizeLimit).getTargetQuality(duration, inputBitRate);
this.upperSizeLimit = upperSizeLimit;
@@ -29,10 +29,6 @@ public final class VideoUtil {
return Math.min(duration, VideoConstants.VIDEO_MAX_RECORD_LENGTH_S);
}
public static int getMaxVideoUploadDurationInSeconds() {
return Math.toIntExact(TimeUnit.MINUTES.toSeconds(10));
}
private static Size screenSize() {
DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics();
return new Size(metrics.widthPixels, metrics.heightPixels);
@@ -23,6 +23,7 @@ import java.io.IOException;
import java.io.OutputStream;
import java.text.NumberFormat;
import java.util.Locale;
import java.util.concurrent.TimeUnit;
@RequiresApi(26)
public final class StreamingTranscoder {
@@ -58,8 +59,13 @@ public final class StreamingTranscoder {
throw new VideoSourceException("Unable to read datasource", e);
}
if (options != null && options.endTimeUs != 0) {
this.duration = TimeUnit.MICROSECONDS.toMillis(options.endTimeUs - options.startTimeUs);
} else {
this.duration = getDuration(mediaMetadataRetriever);
}
this.inSize = dataSource.getSize();
this.duration = getDuration(mediaMetadataRetriever);
this.inputBitRate = VideoBitRateCalculator.bitRate(inSize, duration);
this.targetQuality = new VideoBitRateCalculator(upperSizeLimit).getTargetQuality(duration, inputBitRate);
this.upperSizeLimit = upperSizeLimit;
@@ -7,11 +7,6 @@ import org.thoughtcrime.securesms.video.videoconverter.utils.VideoConstants;
*/
public final class VideoBitRateCalculator {
private static final int MAXIMUM_TARGET_VIDEO_BITRATE = VideoConstants.VIDEO_BIT_RATE;
private static final int LOW_RES_TARGET_VIDEO_BITRATE = 1_750_000;
private static final int MINIMUM_TARGET_VIDEO_BITRATE = 500_000;
private static final int LOW_RES_OUTPUT_FORMAT = 480;
private final long upperFileSizeLimitWithMargin;
public VideoBitRateCalculator(long upperFileSizeLimit) {
@@ -22,13 +17,13 @@ public final class VideoBitRateCalculator {
* Gets the output quality of a video of the given {@param duration}.
*/
public TranscodingQuality getTargetQuality(long duration, int inputTotalBitRate) {
int maxVideoBitRate = Math.min(MAXIMUM_TARGET_VIDEO_BITRATE, inputTotalBitRate - VideoConstants.AUDIO_BIT_RATE);
int minVideoBitRate = Math.min(MINIMUM_TARGET_VIDEO_BITRATE, maxVideoBitRate);
int maxVideoBitRate = Math.min(VideoConstants.VIDEO_TARGET_BIT_RATE, inputTotalBitRate - VideoConstants.AUDIO_BIT_RATE);
int minVideoBitRate = Math.min(VideoConstants.VIDEO_MINIMUM_TARGET_BIT_RATE, maxVideoBitRate);
int targetVideoBitRate = Math.max(minVideoBitRate, Math.min(getTargetVideoBitRate(upperFileSizeLimitWithMargin, duration), maxVideoBitRate));
int bitRateRange = maxVideoBitRate - minVideoBitRate;
double quality = bitRateRange == 0 ? 1 : (targetVideoBitRate - minVideoBitRate) / (double) bitRateRange;
int outputResolution = targetVideoBitRate < LOW_RES_TARGET_VIDEO_BITRATE ? LOW_RES_OUTPUT_FORMAT : VideoConstants.VIDEO_SHORT_EDGE;
int outputResolution = targetVideoBitRate < VideoConstants.LOW_RES_TARGET_VIDEO_BITRATE ? VideoConstants.LOW_RES_OUTPUT_FORMAT : VideoConstants.VIDEO_SHORT_EDGE;
return new TranscodingQuality(targetVideoBitRate, VideoConstants.AUDIO_BIT_RATE, Math.max(0, Math.min(quality, 1)), duration, outputResolution);
}
@@ -47,4 +42,8 @@ public final class VideoBitRateCalculator {
return (int) (bytes * 8 / (durationMs / 1000f));
}
public int getMaxVideoUploadDurationInSeconds() {
long totalMinimumBitrate = VideoConstants.VIDEO_MINIMUM_TARGET_BIT_RATE + VideoConstants.AUDIO_BIT_RATE;
return Math.toIntExact((upperFileSizeLimitWithMargin * 8) / totalMinimumBitrate);
}
}
@@ -7,13 +7,16 @@ package org.thoughtcrime.securesms.video.videoconverter.utils
import android.media.MediaFormat
object VideoConstants {
const val AUDIO_BIT_RATE = 192000
const val AUDIO_BIT_RATE = 192_000
const val VIDEO_FRAME_RATE = 30
const val VIDEO_BIT_RATE = 2000000
const val VIDEO_TARGET_BIT_RATE = 2_000_000
const val VIDEO_MINIMUM_TARGET_BIT_RATE = 500_000
const val LOW_RES_TARGET_VIDEO_BITRATE = 1_750_000
const val LOW_RES_OUTPUT_FORMAT = 480
const val VIDEO_SHORT_EDGE = 720
const val VIDEO_LONG_EDGE = 1280
const val VIDEO_MAX_RECORD_LENGTH_S = 60
const val TOTAL_BYTES_PER_SECOND = VIDEO_BIT_RATE / 8 + AUDIO_BIT_RATE / 8
const val TOTAL_BYTES_PER_SECOND = VIDEO_TARGET_BIT_RATE / 8 + AUDIO_BIT_RATE / 8
const val VIDEO_MIME_TYPE = MediaFormat.MIMETYPE_VIDEO_AVC
const val AUDIO_MIME_TYPE = MediaFormat.MIMETYPE_AUDIO_AAC
const val RECORDED_VIDEO_CONTENT_TYPE: String = "video/mp4"