55
66import androidx .annotation .NonNull ;
77
8+ import com .google .android .exoplayer2 .util .MimeTypes ;
9+
810import org .greenrobot .eventbus .EventBus ;
911import org .signal .core .util .logging .Log ;
1012import org .thoughtcrime .securesms .R ;
1113import org .thoughtcrime .securesms .attachments .Attachment ;
1214import org .thoughtcrime .securesms .attachments .AttachmentId ;
1315import org .thoughtcrime .securesms .attachments .DatabaseAttachment ;
16+ import org .thoughtcrime .securesms .crypto .AttachmentSecret ;
17+ import org .thoughtcrime .securesms .crypto .AttachmentSecretProvider ;
18+ import org .thoughtcrime .securesms .crypto .ModernDecryptingPartInputStream ;
19+ import org .thoughtcrime .securesms .crypto .ModernEncryptingPartOutputStream ;
1420import org .thoughtcrime .securesms .database .AttachmentDatabase ;
1521import org .thoughtcrime .securesms .database .DatabaseFactory ;
1622import org .thoughtcrime .securesms .events .PartProgressEvent ;
2632import org .thoughtcrime .securesms .transport .UndeliverableMessageException ;
2733import org .thoughtcrime .securesms .util .BitmapDecodingException ;
2834import org .thoughtcrime .securesms .util .BitmapUtil ;
35+ import org .thoughtcrime .securesms .util .FeatureFlags ;
2936import org .thoughtcrime .securesms .util .MediaUtil ;
37+ import org .thoughtcrime .securesms .util .MemoryFileDescriptor ;
3038import org .thoughtcrime .securesms .util .MemoryFileDescriptor .MemoryFileException ;
3139import org .thoughtcrime .securesms .video .InMemoryTranscoder ;
32- import org .thoughtcrime .securesms .video .VideoSizeException ;
40+ import org .thoughtcrime .securesms .video .StreamingTranscoder ;
41+ import org .thoughtcrime .securesms .video .TranscoderCancelationSignal ;
42+ import org .thoughtcrime .securesms .video .TranscoderOptions ;
3343import org .thoughtcrime .securesms .video .VideoSourceException ;
3444import org .thoughtcrime .securesms .video .videoconverter .EncodingException ;
3545
3646import java .io .ByteArrayInputStream ;
47+ import java .io .File ;
3748import java .io .IOException ;
49+ import java .io .OutputStream ;
50+ import java .util .Objects ;
3851import java .util .concurrent .TimeUnit ;
3952
4053public final class AttachmentCompressionJob extends BaseJob {
@@ -177,7 +190,7 @@ private void scaleAndStripExif(@NonNull AttachmentDatabase attachmentDatabase,
177190 @ NonNull DatabaseAttachment attachment ,
178191 @ NonNull MediaConstraints constraints ,
179192 @ NonNull EventBus eventBus ,
180- @ NonNull InMemoryTranscoder . CancelationSignal cancelationSignal )
193+ @ NonNull TranscoderCancelationSignal cancelationSignal )
181194 throws UndeliverableMessageException
182195 {
183196 AttachmentDatabase .TransformProperties transformProperties = attachment .getTransformProperties ();
@@ -196,34 +209,73 @@ private void scaleAndStripExif(@NonNull AttachmentDatabase attachmentDatabase,
196209 notification .setIndeterminateProgress ();
197210
198211 try (MediaDataSource dataSource = attachmentDatabase .mediaDataSourceFor (attachment .getAttachmentId ())) {
199-
200212 if (dataSource == null ) {
201213 throw new UndeliverableMessageException ("Cannot get media data source for attachment." );
202214 }
203215
204216 allowSkipOnFailure = !transformProperties .isVideoEdited ();
205- InMemoryTranscoder . Options options = null ;
217+ TranscoderOptions options = null ;
206218 if (transformProperties .isVideoTrim ()) {
207- options = new InMemoryTranscoder . Options (transformProperties .getVideoTrimStartTimeUs (), transformProperties .getVideoTrimEndTimeUs ());
219+ options = new TranscoderOptions (transformProperties .getVideoTrimStartTimeUs (), transformProperties .getVideoTrimEndTimeUs ());
208220 }
209221
210- try (InMemoryTranscoder transcoder = new InMemoryTranscoder (context , dataSource , options , constraints .getCompressedVideoMaxSize (context ))) {
222+ if (FeatureFlags .useStreamingVideoMuxer () || !MemoryFileDescriptor .supported ()) {
223+ StreamingTranscoder transcoder = new StreamingTranscoder (dataSource , options , constraints .getCompressedVideoMaxSize (context ));
224+
211225 if (transcoder .isTranscodeRequired ()) {
212- MediaStream mediaStream = transcoder .transcode (percent -> {
213- notification .setProgress (100 , percent );
214- eventBus .postSticky (new PartProgressEvent (attachment ,
215- PartProgressEvent .Type .COMPRESSION ,
216- 100 ,
217- percent ));
218- }, cancelationSignal );
219-
220- attachmentDatabase .updateAttachmentData (attachment , mediaStream , transformProperties .isVideoEdited ());
226+ Log .i (TAG , "Compressing with streaming muxer" );
227+ AttachmentSecret attachmentSecret = AttachmentSecretProvider .getInstance (context ).getOrCreateAttachmentSecret ();
228+
229+ File file = DatabaseFactory .getAttachmentDatabase (context )
230+ .newFile ();
231+ file .deleteOnExit ();
232+
233+ try {
234+ try (OutputStream outputStream = ModernEncryptingPartOutputStream .createFor (attachmentSecret , file , true ).second ) {
235+ transcoder .transcode (percent -> {
236+ notification .setProgress (100 , percent );
237+ eventBus .postSticky (new PartProgressEvent (attachment ,
238+ PartProgressEvent .Type .COMPRESSION ,
239+ 100 ,
240+ percent ));
241+ }, outputStream , cancelationSignal );
242+ }
243+
244+ MediaStream mediaStream = new MediaStream (ModernDecryptingPartInputStream .createFor (attachmentSecret , file , 0 ), MimeTypes .VIDEO_MP4 , 0 , 0 );
245+ attachmentDatabase .updateAttachmentData (attachment , mediaStream , transformProperties .isVideoEdited ());
246+ } finally {
247+ if (!file .delete ()) {
248+ Log .w (TAG , "Failed to delete temp file" );
249+ }
250+ }
251+
221252 attachmentDatabase .markAttachmentAsTransformed (attachment .getAttachmentId ());
222- DatabaseAttachment updatedAttachment = attachmentDatabase .getAttachment (attachment .getAttachmentId ());
223- if (updatedAttachment == null ) {
224- throw new AssertionError ();
253+
254+ return Objects .requireNonNull (attachmentDatabase .getAttachment (attachment .getAttachmentId ()));
255+ } else {
256+ Log .i (TAG , "Transcode was not required" );
257+ }
258+ } else {
259+ try (InMemoryTranscoder transcoder = new InMemoryTranscoder (context , dataSource , options , constraints .getCompressedVideoMaxSize (context ))) {
260+ if (transcoder .isTranscodeRequired ()) {
261+ Log .i (TAG , "Compressing with android in-memory muxer" );
262+
263+ MediaStream mediaStream = transcoder .transcode (percent -> {
264+ notification .setProgress (100 , percent );
265+ eventBus .postSticky (new PartProgressEvent (attachment ,
266+ PartProgressEvent .Type .COMPRESSION ,
267+ 100 ,
268+ percent ));
269+ }, cancelationSignal );
270+
271+ attachmentDatabase .updateAttachmentData (attachment , mediaStream , transformProperties .isVideoEdited ());
272+
273+ attachmentDatabase .markAttachmentAsTransformed (attachment .getAttachmentId ());
274+
275+ return Objects .requireNonNull (attachmentDatabase .getAttachment (attachment .getAttachmentId ()));
276+ } else {
277+ Log .i (TAG , "Transcode was not required (in-memory transcoder)" );
225278 }
226- return updatedAttachment ;
227279 }
228280 }
229281 }
@@ -237,7 +289,7 @@ private void scaleAndStripExif(@NonNull AttachmentDatabase attachmentDatabase,
237289 throw new UndeliverableMessageException ("Failed to transcode and cannot skip due to editing" , e );
238290 }
239291 }
240- } catch (IOException | MmsException | VideoSizeException e ) {
292+ } catch (IOException | MmsException e ) {
241293 throw new UndeliverableMessageException ("Failed to transcode" , e );
242294 }
243295 return attachment ;
0 commit comments