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 f19e727460..b9c49357e4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentCompressionJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentCompressionJob.java @@ -42,10 +42,8 @@ import org.thoughtcrime.securesms.video.InMemoryTranscoder; import org.thoughtcrime.securesms.video.StreamingTranscoder; import org.thoughtcrime.securesms.video.TranscoderCancelationSignal; import org.thoughtcrime.securesms.video.TranscoderOptions; -import org.thoughtcrime.securesms.video.exceptions.VideoPostProcessingException; import org.thoughtcrime.securesms.video.exceptions.VideoSourceException; import org.thoughtcrime.securesms.video.postprocessing.Mp4FaststartPostProcessor; -import org.thoughtcrime.securesms.video.exceptions.VideoSourceException; import org.thoughtcrime.securesms.video.videoconverter.EncodingException; import java.io.ByteArrayInputStream; @@ -263,7 +261,7 @@ public final class AttachmentCompressionJob extends BaseJob { Log.i(TAG, "Compressing with streaming muxer"); AttachmentSecret attachmentSecret = AttachmentSecretProvider.getInstance(context).getOrCreateAttachmentSecret(); - File file = SignalDatabase.attachments().newFile(context); + File file = AttachmentTable.newFile(context); file.deleteOnExit(); boolean faststart = false; @@ -291,9 +289,10 @@ public final class AttachmentCompressionJob extends BaseJob { } catch (IOException e) { throw new RuntimeException(e); } - }, file.length()); + }); - try (MediaStream mediaStream = new MediaStream(postProcessor.process(), MimeTypes.VIDEO_MP4, 0, 0, true)) { + final long plaintextLength = ModernEncryptingPartOutputStream.getPlaintextLength(file.length()); + try (MediaStream mediaStream = new MediaStream(postProcessor.process(plaintextLength), MimeTypes.VIDEO_MP4, 0, 0, true)) { attachmentDatabase.updateAttachmentData(attachment, mediaStream, true); faststart = true; } diff --git a/app/src/main/java/org/thoughtcrime/securesms/video/InMemoryTranscoder.java b/app/src/main/java/org/thoughtcrime/securesms/video/InMemoryTranscoder.java index 9cd415b03b..69d48f4e26 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/video/InMemoryTranscoder.java +++ b/app/src/main/java/org/thoughtcrime/securesms/video/InMemoryTranscoder.java @@ -179,9 +179,9 @@ public final class InMemoryTranscoder implements Closeable { Log.w(TAG, "IOException thrown while creating FileInputStream.", e); throw new VideoPostProcessingException("Exception while opening InputStream!", e); } - }, memoryFile.size()); + }); - return new MediaStream(postProcessor.process(), MimeTypes.VIDEO_MP4, 0, 0, true); + return new MediaStream(postProcessor.process(outSize), MimeTypes.VIDEO_MP4, 0, 0, true); } catch (VideoPostProcessingException e) { Log.w(TAG, "Exception thrown during post processing.", e); final Throwable cause = e.getCause(); diff --git a/video/app/src/main/java/org/thoughtcrime/video/app/transcode/TranscodeWorker.kt b/video/app/src/main/java/org/thoughtcrime/video/app/transcode/TranscodeWorker.kt index a9eae7a776..78a69db664 100644 --- a/video/app/src/main/java/org/thoughtcrime/video/app/transcode/TranscodeWorker.kt +++ b/video/app/src/main/java/org/thoughtcrime/video/app/transcode/TranscodeWorker.kt @@ -113,10 +113,7 @@ class TranscodeWorker(ctx: Context, params: WorkerParameters) : CoroutineWorker( return Result.failure() } - tempFileLength = tempFileStream.readLength() ?: run { - Log.w(TAG, "$logPrefix Could not read file length of temp file descriptor!") - return Result.failure() - } + tempFileLength = tempFileStream.readLength() } val finalFile = createFile(Uri.parse(outputDirUri), finalFilename) ?: run { Log.w(TAG, "$logPrefix Could not create final file for faststart processing!") @@ -129,7 +126,7 @@ class TranscodeWorker(ctx: Context, params: WorkerParameters) : CoroutineWorker( } val inputStreamFactory = { applicationContext.contentResolver.openInputStream(tempFile.uri) ?: throw IOException("Could not open temp file for reading!") } - val bytesCopied = Mp4FaststartPostProcessor(inputStreamFactory, tempFileLength).processAndWriteTo(finalFileStream) + val bytesCopied = Mp4FaststartPostProcessor(inputStreamFactory).processAndWriteTo(finalFileStream) if (bytesCopied != tempFileLength) { Log.w(TAG, "$logPrefix Postprocessing failed! Original transcoded filesize ($tempFileLength) did not match postprocessed filesize ($bytesCopied)") diff --git a/video/lib/src/main/java/org/thoughtcrime/securesms/video/postprocessing/Mp4FaststartPostProcessor.kt b/video/lib/src/main/java/org/thoughtcrime/securesms/video/postprocessing/Mp4FaststartPostProcessor.kt index ba8922560a..a9b1353d65 100644 --- a/video/lib/src/main/java/org/thoughtcrime/securesms/video/postprocessing/Mp4FaststartPostProcessor.kt +++ b/video/lib/src/main/java/org/thoughtcrime/securesms/video/postprocessing/Mp4FaststartPostProcessor.kt @@ -5,9 +5,9 @@ package org.thoughtcrime.securesms.video.postprocessing +import org.signal.core.util.readLength import org.signal.libsignal.media.Mp4Sanitizer import org.signal.libsignal.media.SanitizedMetadata -import org.thoughtcrime.securesms.video.exceptions.VideoPostProcessingException import java.io.ByteArrayInputStream import java.io.FilterInputStream import java.io.IOException @@ -18,31 +18,22 @@ import java.io.SequenceInputStream /** * A post processor that takes a stream of bytes, and using [Mp4Sanitizer], moves the metadata to the front of the file. * - * @property inputStreamFactory factory for the [InputStream]. May be called multiple times. - * @property inputLength the exact length of the [InputStream] + * @property inputStreamFactory factory for the [InputStream]. Expected to be called multiple times. */ -class Mp4FaststartPostProcessor(private val inputStreamFactory: InputStreamFactory, private val inputLength: Long) { +class Mp4FaststartPostProcessor(private val inputStreamFactory: InputStreamFactory) { /** - * It is the responsibility of the called to close the resulting [InputStream] + * It is the responsibility of the caller to close the resulting [InputStream]. */ - fun process(): InputStream { - val metadata: SanitizedMetadata? - inputStreamFactory.create().use { - metadata = Mp4Sanitizer.sanitize(it, inputLength) - } - if (metadata?.sanitizedMetadata == null) { - throw VideoPostProcessingException("Mp4Sanitizer could not parse media metadata!") - } - + fun process(inputLength: Long = calculateStreamLength(inputStreamFactory.create())): SequenceInputStream { + val metadata = sanitizeMetadata(inputStreamFactory.create(), inputLength) val inputStream = inputStreamFactory.create() inputStream.skip(metadata.dataOffset) - return SequenceInputStream(ByteArrayInputStream(metadata.sanitizedMetadata), LimitedInputStream(inputStream, metadata.dataLength)) } - fun processAndWriteTo(outputStream: OutputStream): Long { - process().use { inStream -> + fun processAndWriteTo(outputStream: OutputStream, inputLength: Long = calculateStreamLength(inputStreamFactory.create())): Long { + process(inputLength).use { inStream -> return inStream.copyTo(outputStream) } } @@ -53,6 +44,20 @@ class Mp4FaststartPostProcessor(private val inputStreamFactory: InputStreamFacto companion object { const val TAG = "Mp4Faststart" + + @JvmStatic + fun calculateStreamLength(inputStream: InputStream): Long { + inputStream.use { + return it.readLength() + } + } + + @JvmStatic + private fun sanitizeMetadata(inputStream: InputStream, inputLength: Long): SanitizedMetadata { + inputStream.use { + return Mp4Sanitizer.sanitize(it, inputLength) + } + } } private class LimitedInputStream(innerStream: InputStream, limit: Long) : FilterInputStream(innerStream) {