Search in sources :

Example 26 with AttachmentDatabase

use of org.thoughtcrime.securesms.database.AttachmentDatabase in project Signal-Android by WhisperSystems.

the class AttachmentCompressionJob method transcodeVideoIfNeededToDatabase.

@NonNull
private static DatabaseAttachment transcodeVideoIfNeededToDatabase(@NonNull Context context, @NonNull AttachmentDatabase attachmentDatabase, @NonNull DatabaseAttachment attachment, @NonNull MediaConstraints constraints, @NonNull EventBus eventBus, @NonNull TranscoderCancelationSignal cancelationSignal) throws UndeliverableMessageException {
    AttachmentDatabase.TransformProperties transformProperties = attachment.getTransformProperties();
    boolean allowSkipOnFailure = false;
    if (!MediaConstraints.isVideoTranscodeAvailable()) {
        if (transformProperties.isVideoEdited()) {
            throw new UndeliverableMessageException("Video edited, but transcode is not available");
        }
        return attachment;
    }
    try (NotificationController notification = GenericForegroundService.startForegroundTask(context, context.getString(R.string.AttachmentUploadJob_compressing_video_start))) {
        notification.setIndeterminateProgress();
        try (MediaDataSource dataSource = attachmentDatabase.mediaDataSourceFor(attachment.getAttachmentId())) {
            if (dataSource == null) {
                throw new UndeliverableMessageException("Cannot get media data source for attachment.");
            }
            allowSkipOnFailure = !transformProperties.isVideoEdited();
            TranscoderOptions options = null;
            if (transformProperties.isVideoTrim()) {
                options = new TranscoderOptions(transformProperties.getVideoTrimStartTimeUs(), transformProperties.getVideoTrimEndTimeUs());
            }
            if (FeatureFlags.useStreamingVideoMuxer() || !MemoryFileDescriptor.supported()) {
                StreamingTranscoder transcoder = new StreamingTranscoder(dataSource, options, constraints.getCompressedVideoMaxSize(context));
                if (transcoder.isTranscodeRequired()) {
                    Log.i(TAG, "Compressing with streaming muxer");
                    AttachmentSecret attachmentSecret = AttachmentSecretProvider.getInstance(context).getOrCreateAttachmentSecret();
                    File file = SignalDatabase.attachments().newFile();
                    file.deleteOnExit();
                    try {
                        try (OutputStream outputStream = ModernEncryptingPartOutputStream.createFor(attachmentSecret, file, true).second) {
                            transcoder.transcode(percent -> {
                                notification.setProgress(100, percent);
                                eventBus.postSticky(new PartProgressEvent(attachment, PartProgressEvent.Type.COMPRESSION, 100, percent));
                            }, outputStream, cancelationSignal);
                        }
                        MediaStream mediaStream = new MediaStream(ModernDecryptingPartInputStream.createFor(attachmentSecret, file, 0), MimeTypes.VIDEO_MP4, 0, 0);
                        attachmentDatabase.updateAttachmentData(attachment, mediaStream, transformProperties.isVideoEdited());
                    } finally {
                        if (!file.delete()) {
                            Log.w(TAG, "Failed to delete temp file");
                        }
                    }
                    attachmentDatabase.markAttachmentAsTransformed(attachment.getAttachmentId());
                    return Objects.requireNonNull(attachmentDatabase.getAttachment(attachment.getAttachmentId()));
                } else {
                    Log.i(TAG, "Transcode was not required");
                }
            } else {
                try (InMemoryTranscoder transcoder = new InMemoryTranscoder(context, dataSource, options, constraints.getCompressedVideoMaxSize(context))) {
                    if (transcoder.isTranscodeRequired()) {
                        Log.i(TAG, "Compressing with android in-memory muxer");
                        MediaStream mediaStream = transcoder.transcode(percent -> {
                            notification.setProgress(100, percent);
                            eventBus.postSticky(new PartProgressEvent(attachment, PartProgressEvent.Type.COMPRESSION, 100, percent));
                        }, cancelationSignal);
                        attachmentDatabase.updateAttachmentData(attachment, mediaStream, transformProperties.isVideoEdited());
                        attachmentDatabase.markAttachmentAsTransformed(attachment.getAttachmentId());
                        return Objects.requireNonNull(attachmentDatabase.getAttachment(attachment.getAttachmentId()));
                    } else {
                        Log.i(TAG, "Transcode was not required (in-memory transcoder)");
                    }
                }
            }
        }
    } catch (VideoSourceException | EncodingException | MemoryFileException e) {
        if (attachment.getSize() > constraints.getVideoMaxSize(context)) {
            throw new UndeliverableMessageException("Duration not found, attachment too large to skip transcode", e);
        } else {
            if (allowSkipOnFailure) {
                Log.w(TAG, "Problem with video source, but video small enough to skip transcode", e);
            } else {
                throw new UndeliverableMessageException("Failed to transcode and cannot skip due to editing", e);
            }
        }
    } catch (IOException | MmsException e) {
        throw new UndeliverableMessageException("Failed to transcode", e);
    }
    return attachment;
}
Also used : VideoSourceException(org.thoughtcrime.securesms.video.VideoSourceException) MemoryFileException(org.thoughtcrime.securesms.util.MemoryFileDescriptor.MemoryFileException) EncodingException(org.thoughtcrime.securesms.video.videoconverter.EncodingException) OutputStream(java.io.OutputStream) ModernEncryptingPartOutputStream(org.thoughtcrime.securesms.crypto.ModernEncryptingPartOutputStream) PartProgressEvent(org.thoughtcrime.securesms.events.PartProgressEvent) IOException(java.io.IOException) AttachmentDatabase(org.thoughtcrime.securesms.database.AttachmentDatabase) TranscoderOptions(org.thoughtcrime.securesms.video.TranscoderOptions) MmsException(org.thoughtcrime.securesms.mms.MmsException) InMemoryTranscoder(org.thoughtcrime.securesms.video.InMemoryTranscoder) MediaStream(org.thoughtcrime.securesms.mms.MediaStream) MediaDataSource(android.media.MediaDataSource) UndeliverableMessageException(org.thoughtcrime.securesms.transport.UndeliverableMessageException) StreamingTranscoder(org.thoughtcrime.securesms.video.StreamingTranscoder) AttachmentSecret(org.thoughtcrime.securesms.crypto.AttachmentSecret) File(java.io.File) NotificationController(org.thoughtcrime.securesms.service.NotificationController) NonNull(androidx.annotation.NonNull)

Example 27 with AttachmentDatabase

use of org.thoughtcrime.securesms.database.AttachmentDatabase in project Signal-Android by WhisperSystems.

the class PartDataSource method open.

@Override
public long open(DataSpec dataSpec) throws IOException {
    this.uri = dataSpec.uri;
    AttachmentDatabase attachmentDatabase = SignalDatabase.attachments();
    PartUriParser partUri = new PartUriParser(uri);
    Attachment attachment = attachmentDatabase.getAttachment(partUri.getPartId());
    if (attachment == null)
        throw new IOException("Attachment not found");
    this.inputSteam = attachmentDatabase.getAttachmentStream(partUri.getPartId(), dataSpec.position);
    if (listener != null) {
        listener.onTransferStart(this, dataSpec, false);
    }
    if (attachment.getSize() - dataSpec.position <= 0)
        throw new EOFException("No more data");
    return attachment.getSize() - dataSpec.position;
}
Also used : PartUriParser(org.thoughtcrime.securesms.mms.PartUriParser) EOFException(java.io.EOFException) Attachment(org.thoughtcrime.securesms.attachments.Attachment) IOException(java.io.IOException) AttachmentDatabase(org.thoughtcrime.securesms.database.AttachmentDatabase)

Example 28 with AttachmentDatabase

use of org.thoughtcrime.securesms.database.AttachmentDatabase in project Signal-Android by signalapp.

the class MessageSender method preUploadPushAttachment.

/**
 * @return A result if the attachment was enqueued, or null if it failed to enqueue or shouldn't
 *         be enqueued (like in the case of a local self-send).
 */
@Nullable
public static PreUploadResult preUploadPushAttachment(@NonNull Context context, @NonNull Attachment attachment, @Nullable Recipient recipient) {
    if (isLocalSelfSend(context, recipient, false)) {
        return null;
    }
    Log.i(TAG, "Pre-uploading attachment for " + (recipient != null ? recipient.getId() : "null"));
    try {
        AttachmentDatabase attachmentDatabase = SignalDatabase.attachments();
        DatabaseAttachment databaseAttachment = attachmentDatabase.insertAttachmentForPreUpload(attachment);
        Job compressionJob = AttachmentCompressionJob.fromAttachment(databaseAttachment, false, -1);
        Job resumableUploadSpecJob = new ResumableUploadSpecJob();
        Job uploadJob = new AttachmentUploadJob(databaseAttachment.getAttachmentId());
        ApplicationDependencies.getJobManager().startChain(compressionJob).then(resumableUploadSpecJob).then(uploadJob).enqueue();
        return new PreUploadResult(databaseAttachment.getAttachmentId(), Arrays.asList(compressionJob.getId(), resumableUploadSpecJob.getId(), uploadJob.getId()));
    } catch (MmsException e) {
        Log.w(TAG, "preUploadPushAttachment() - Failed to upload!", e);
        return null;
    }
}
Also used : MmsException(org.thoughtcrime.securesms.mms.MmsException) DatabaseAttachment(org.thoughtcrime.securesms.attachments.DatabaseAttachment) ResumableUploadSpecJob(org.thoughtcrime.securesms.jobs.ResumableUploadSpecJob) AttachmentMarkUploadedJob(org.thoughtcrime.securesms.jobs.AttachmentMarkUploadedJob) ReactionSendJob(org.thoughtcrime.securesms.jobs.ReactionSendJob) PushMediaSendJob(org.thoughtcrime.securesms.jobs.PushMediaSendJob) Job(org.thoughtcrime.securesms.jobmanager.Job) ResumableUploadSpecJob(org.thoughtcrime.securesms.jobs.ResumableUploadSpecJob) PushGroupSendJob(org.thoughtcrime.securesms.jobs.PushGroupSendJob) AttachmentCompressionJob(org.thoughtcrime.securesms.jobs.AttachmentCompressionJob) ProfileKeySendJob(org.thoughtcrime.securesms.jobs.ProfileKeySendJob) RemoteDeleteSendJob(org.thoughtcrime.securesms.jobs.RemoteDeleteSendJob) MmsSendJob(org.thoughtcrime.securesms.jobs.MmsSendJob) AttachmentUploadJob(org.thoughtcrime.securesms.jobs.AttachmentUploadJob) PushTextSendJob(org.thoughtcrime.securesms.jobs.PushTextSendJob) AttachmentCopyJob(org.thoughtcrime.securesms.jobs.AttachmentCopyJob) SmsSendJob(org.thoughtcrime.securesms.jobs.SmsSendJob) AttachmentDatabase(org.thoughtcrime.securesms.database.AttachmentDatabase) AttachmentUploadJob(org.thoughtcrime.securesms.jobs.AttachmentUploadJob) Nullable(androidx.annotation.Nullable)

Example 29 with AttachmentDatabase

use of org.thoughtcrime.securesms.database.AttachmentDatabase in project Signal-Android by signalapp.

the class AttachmentCompressionJob method onRun.

@Override
public void onRun() throws Exception {
    Log.d(TAG, "Running for: " + attachmentId);
    AttachmentDatabase database = SignalDatabase.attachments();
    DatabaseAttachment databaseAttachment = database.getAttachment(attachmentId);
    if (databaseAttachment == null) {
        throw new UndeliverableMessageException("Cannot find the specified attachment.");
    }
    if (databaseAttachment.getTransformProperties().shouldSkipTransform()) {
        Log.i(TAG, "Skipping at the direction of the TransformProperties.");
        return;
    }
    MediaConstraints mediaConstraints = mms ? MediaConstraints.getMmsMediaConstraints(mmsSubscriptionId) : MediaConstraints.getPushMediaConstraints(SentMediaQuality.fromCode(databaseAttachment.getTransformProperties().getSentMediaQuality()));
    compress(database, mediaConstraints, databaseAttachment);
}
Also used : MediaConstraints(org.thoughtcrime.securesms.mms.MediaConstraints) UndeliverableMessageException(org.thoughtcrime.securesms.transport.UndeliverableMessageException) DatabaseAttachment(org.thoughtcrime.securesms.attachments.DatabaseAttachment) AttachmentDatabase(org.thoughtcrime.securesms.database.AttachmentDatabase)

Example 30 with AttachmentDatabase

use of org.thoughtcrime.securesms.database.AttachmentDatabase in project Signal-Android by signalapp.

the class AttachmentDownloadJob method markFailed.

private void markFailed(long messageId, AttachmentId attachmentId) {
    try {
        AttachmentDatabase database = SignalDatabase.attachments();
        database.setTransferProgressFailed(attachmentId, messageId);
    } catch (MmsException e) {
        Log.w(TAG, e);
    }
}
Also used : MmsException(org.thoughtcrime.securesms.mms.MmsException) AttachmentDatabase(org.thoughtcrime.securesms.database.AttachmentDatabase)

Aggregations

AttachmentDatabase (org.thoughtcrime.securesms.database.AttachmentDatabase)35 DatabaseAttachment (org.thoughtcrime.securesms.attachments.DatabaseAttachment)20 Attachment (org.thoughtcrime.securesms.attachments.Attachment)14 MmsException (org.thoughtcrime.securesms.mms.MmsException)13 IOException (java.io.IOException)12 AttachmentId (org.thoughtcrime.securesms.attachments.AttachmentId)10 NonNull (androidx.annotation.NonNull)6 LinkedList (java.util.LinkedList)6 MessageDatabase (org.thoughtcrime.securesms.database.MessageDatabase)6 Job (org.thoughtcrime.securesms.jobmanager.Job)6 UndeliverableMessageException (org.thoughtcrime.securesms.transport.UndeliverableMessageException)6 File (java.io.File)5 PartProgressEvent (org.thoughtcrime.securesms.events.PartProgressEvent)5 Stream (com.annimon.stream.Stream)4 MessageRecord (org.thoughtcrime.securesms.database.model.MessageRecord)4 MediaStream (org.thoughtcrime.securesms.mms.MediaStream)4 SignalServiceAttachmentPointer (org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer)4 Nullable (androidx.annotation.Nullable)3 InputStream (java.io.InputStream)3 List (java.util.List)3