From 960c1df5e740ab108b8b189957987cdeb93530e6 Mon Sep 17 00:00:00 2001 From: Nicholas Tinsley Date: Tue, 9 Jan 2024 16:57:45 -0500 Subject: [PATCH] Apply faststart to videos transcoded using Streams. --- .../jobs/AttachmentCompressionJob.java | 29 +++++++++++++++++-- .../securesms/video/StreamingTranscoder.java | 4 --- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentCompressionJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentCompressionJob.java index fe238d627b..61cd482229 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentCompressionJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentCompressionJob.java @@ -9,8 +9,13 @@ import androidx.annotation.Nullable; import androidx.annotation.WorkerThread; import androidx.media3.common.MimeTypes; +import com.google.common.io.ByteStreams; + import org.greenrobot.eventbus.EventBus; import org.signal.core.util.logging.Log; +import org.signal.libsignal.media.Mp4Sanitizer; +import org.signal.libsignal.media.ParseException; +import org.signal.libsignal.media.SanitizedMetadata; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.attachments.Attachment; import org.thoughtcrime.securesms.attachments.AttachmentId; @@ -48,7 +53,9 @@ import org.thoughtcrime.securesms.video.videoconverter.EncodingException; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; +import java.io.InputStream; import java.io.OutputStream; +import java.io.SequenceInputStream; import java.util.Objects; import java.util.concurrent.TimeUnit; @@ -256,6 +263,7 @@ public final class AttachmentCompressionJob extends BaseJob { File file = SignalDatabase.attachments().newFile(context); file.deleteOnExit(); + boolean faststart = false; try { try (OutputStream outputStream = ModernEncryptingPartOutputStream.createFor(attachmentSecret, file, true).second) { transcoder.transcode(percent -> { @@ -274,8 +282,23 @@ public final class AttachmentCompressionJob extends BaseJob { 100, 100)); - try (MediaStream mediaStream = new MediaStream(ModernDecryptingPartInputStream.createFor(attachmentSecret, file, 0), MimeTypes.VIDEO_MP4, 0, 0)) { - attachmentDatabase.updateAttachmentData(attachment, mediaStream, true); + InputStream transcodedFileStream = ModernDecryptingPartInputStream.createFor(attachmentSecret, file, 0); + SanitizedMetadata metadata = null; + try { + metadata = Mp4Sanitizer.sanitize(transcodedFileStream, file.length()); + } catch (ParseException e) { + Log.e(TAG, "Could not parse MP4 file.", e); + } + + if (metadata != null && metadata.getSanitizedMetadata() != null) { + try (MediaStream mediaStream = new MediaStream(new SequenceInputStream(new ByteArrayInputStream(metadata.getSanitizedMetadata()), ByteStreams.limit(ModernDecryptingPartInputStream.createFor(attachmentSecret, file, metadata.getDataOffset()), metadata.getDataLength())), MimeTypes.VIDEO_MP4, 0, 0, true)) { + attachmentDatabase.updateAttachmentData(attachment, mediaStream, true); + faststart = true; + } + } else { + try (MediaStream mediaStream = new MediaStream(ModernDecryptingPartInputStream.createFor(attachmentSecret, file, 0), MimeTypes.VIDEO_MP4, 0, 0)) { + attachmentDatabase.updateAttachmentData(attachment, mediaStream, true); + } } } finally { if (!file.delete()) { @@ -283,7 +306,7 @@ public final class AttachmentCompressionJob extends BaseJob { } } - attachmentDatabase.markAttachmentAsTransformed(attachment.attachmentId, false); + attachmentDatabase.markAttachmentAsTransformed(attachment.attachmentId, faststart); return Objects.requireNonNull(attachmentDatabase.getAttachment(attachment.attachmentId)); } else { diff --git a/app/src/main/java/org/thoughtcrime/securesms/video/StreamingTranscoder.java b/app/src/main/java/org/thoughtcrime/securesms/video/StreamingTranscoder.java index d56a41b91d..8f1690648f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/video/StreamingTranscoder.java +++ b/app/src/main/java/org/thoughtcrime/securesms/video/StreamingTranscoder.java @@ -29,7 +29,6 @@ public final class StreamingTranscoder { private final long duration; private final int inputBitRate; private final VideoBitRateCalculator.Quality targetQuality; - private final long memoryFileEstimate; private final boolean transcodeRequired; private final long fileSizeEstimate; private final @Nullable TranscoderOptions options; @@ -65,7 +64,6 @@ public final class StreamingTranscoder { } this.fileSizeEstimate = targetQuality.getFileSizeEstimate(); - this.memoryFileEstimate = (long) (fileSizeEstimate * 1.1); } public void transcode(@NonNull Progress progress, @@ -136,14 +134,12 @@ public final class StreamingTranscoder { "Output size : %s kB\n" + " of Original : %.1f%%\n" + " of Estimate : %.1f%%\n" + - " of Memory : %.1f%%\n" + "Output bitrate : %s bps", encodeDurationSec, durationSec / encodeDurationSec, numberFormat.format(outSize / 1024), (outSize * 100d) / inSize, (outSize * 100d) / fileSizeEstimate, - (outSize * 100d) / memoryFileEstimate, numberFormat.format(VideoBitRateCalculator.bitRate(outSize, duration)))); if (outSize > upperSizeLimit) {