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;
}
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;
}
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;
}
}
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);
}
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);
}
}
Aggregations