mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-03 00:42:58 +01:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2e0e8c2235 | ||
|
|
5c6962082a | ||
|
|
701bed970b | ||
|
|
d0918dcb7b | ||
|
|
36aae9823b | ||
|
|
b1f5206680 | ||
|
|
38ed75fb64 | ||
|
|
26ab20f860 |
@@ -1,17 +1,7 @@
|
||||
@file:Suppress("UnstableApiUsage")
|
||||
|
||||
import com.android.build.api.dsl.ManagedVirtualDevice
|
||||
import org.gradle.api.file.DirectoryProperty
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.provider.ValueSource
|
||||
import org.gradle.api.provider.ValueSourceParameters
|
||||
import org.gradle.api.tasks.InputDirectory
|
||||
import org.gradle.api.tasks.InputFile
|
||||
import org.gradle.api.tasks.Optional
|
||||
import org.gradle.api.tasks.PathSensitive
|
||||
import org.gradle.api.tasks.PathSensitivity
|
||||
import org.gradle.api.tasks.testing.logging.TestExceptionFormat
|
||||
import java.io.File
|
||||
import java.util.Properties
|
||||
|
||||
plugins {
|
||||
@@ -31,8 +21,8 @@ plugins {
|
||||
apply(from = "static-ips.gradle.kts")
|
||||
|
||||
val canonicalVersionCode = 1651
|
||||
val canonicalVersionName = "7.74.3"
|
||||
val currentHotfixVersion = 0
|
||||
val canonicalVersionName = "7.74.5"
|
||||
val currentHotfixVersion = 2
|
||||
val maxHotfixVersions = 100
|
||||
|
||||
// We don't want versions to ever end in 0 so that they don't conflict with nightly versions
|
||||
|
||||
@@ -74,7 +74,7 @@ public final class AttachmentCompressionJob extends BaseJob {
|
||||
int mmsSubscriptionId)
|
||||
{
|
||||
return new AttachmentCompressionJob(databaseAttachment.attachmentId,
|
||||
MediaUtil.isVideo(databaseAttachment) && MediaConstraints.isVideoTranscodeAvailable(),
|
||||
MediaUtil.isVideo(databaseAttachment) && !databaseAttachment.videoGif && MediaConstraints.isVideoTranscodeAvailable(),
|
||||
mms,
|
||||
mmsSubscriptionId);
|
||||
}
|
||||
|
||||
@@ -458,8 +458,8 @@ class RegistrationViewModel : ViewModel() {
|
||||
}
|
||||
|
||||
fun submitCaptchaToken(context: Context) {
|
||||
val e164 = getCurrentE164() ?: throw IllegalStateException("Can't submit captcha token if no phone number is set!")
|
||||
val captchaToken = store.value.captchaToken ?: throw IllegalStateException("Can't submit captcha token if no captcha token is set!")
|
||||
val e164 = getCurrentE164() ?: return clearChallengesAndBail { Log.w(TAG, "Phone number was null when trying to submit captcha token.") }
|
||||
val captchaToken = store.value.captchaToken ?: return bail { Log.w(TAG, "Captcha token was null when trying to submit captcha token.") }
|
||||
|
||||
store.update {
|
||||
it.copy(captchaToken = null, challengeInProgress = true, inProgress = true)
|
||||
@@ -486,7 +486,7 @@ class RegistrationViewModel : ViewModel() {
|
||||
fun requestAndSubmitPushToken(context: Context) {
|
||||
Log.v(TAG, "validatePushToken()")
|
||||
|
||||
val e164 = getCurrentE164() ?: throw IllegalStateException("Can't submit captcha token if no phone number is set!")
|
||||
val e164 = getCurrentE164() ?: return clearChallengesAndBail { Log.w(TAG, "Phone number was null when trying to submit push token.") }
|
||||
|
||||
viewModelScope.launch {
|
||||
Log.d(TAG, "Getting session in order to perform push token verification…")
|
||||
@@ -1063,6 +1063,22 @@ class RegistrationViewModel : ViewModel() {
|
||||
setInProgress(false)
|
||||
}
|
||||
|
||||
/**
|
||||
* Like [bail], but also clears challenge state. This is needed when challenge handling fails due to missing phone number,
|
||||
* since otherwise the stale challenges would re-trigger the observer on every config change.
|
||||
*/
|
||||
private fun clearChallengesAndBail(logMessage: () -> Unit) {
|
||||
logMessage()
|
||||
store.update {
|
||||
it.copy(
|
||||
inProgress = false,
|
||||
challengesRequested = emptyList(),
|
||||
challengeInProgress = false,
|
||||
captchaToken = null
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun registerWithBackupKey(context: Context, backupKey: String, e164: String?, pin: String?, aciIdentityKeyPair: IdentityKeyPair?, pniIdentityKeyPair: IdentityKeyPair?) {
|
||||
setInProgress(true)
|
||||
|
||||
|
||||
@@ -600,6 +600,7 @@ class EnterPhoneNumberFragment : LoggingFragment(R.layout.fragment_registration_
|
||||
private fun updateEnabledControls(showProgress: Boolean, isReRegister: Boolean) {
|
||||
binding.countryCode.isEnabled = !showProgress
|
||||
binding.number.isEnabled = !showProgress
|
||||
countryPickerView.isEnabled = !showProgress
|
||||
binding.cancelButton.visible = !showProgress && isReRegister
|
||||
}
|
||||
|
||||
|
||||
@@ -150,6 +150,7 @@ public final class MediaConverter {
|
||||
AudioTrackConverter audioTrackConverter = null;
|
||||
|
||||
long mdatContentLength = 0;
|
||||
boolean muxerStarted = false;
|
||||
boolean muxerStopped = false;
|
||||
|
||||
try {
|
||||
@@ -162,13 +163,17 @@ public final class MediaConverter {
|
||||
throw new EncodingException("No video and audio tracks");
|
||||
}
|
||||
|
||||
doExtractDecodeEditEncodeMux(
|
||||
muxerStarted = doExtractDecodeEditEncodeMux(
|
||||
videoTrackConverter,
|
||||
audioTrackConverter,
|
||||
muxer);
|
||||
|
||||
mdatContentLength = muxer.stop();
|
||||
muxerStopped = true;
|
||||
if (muxerStarted) {
|
||||
mdatContentLength = muxer.stop();
|
||||
muxerStopped = true;
|
||||
} else if (mCancelled) {
|
||||
throw new EncodingException("Conversion cancelled before muxing started");
|
||||
}
|
||||
|
||||
} catch (EncodingException | IOException e) {
|
||||
Log.e(TAG, "error converting", e);
|
||||
@@ -204,7 +209,7 @@ public final class MediaConverter {
|
||||
}
|
||||
try {
|
||||
if (muxer != null) {
|
||||
if (!muxerStopped) {
|
||||
if (!muxerStopped && muxerStarted) {
|
||||
muxer.stop();
|
||||
}
|
||||
muxer.release();
|
||||
@@ -225,8 +230,10 @@ public final class MediaConverter {
|
||||
|
||||
/**
|
||||
* Does the actual work for extracting, decoding, encoding and muxing.
|
||||
*
|
||||
* @return true if the muxer was started, false otherwise.
|
||||
*/
|
||||
private void doExtractDecodeEditEncodeMux(
|
||||
private boolean doExtractDecodeEditEncodeMux(
|
||||
final @Nullable VideoTrackConverter videoTrackConverter,
|
||||
final @Nullable AudioTrackConverter audioTrackConverter,
|
||||
final @NonNull Muxer muxer) throws IOException, TranscodingException {
|
||||
@@ -249,7 +256,7 @@ public final class MediaConverter {
|
||||
Log.d(TAG, "loop: " + currentState);
|
||||
}
|
||||
|
||||
if (currentState.equals(oldState)) {
|
||||
if (muxing && currentState.equals(oldState)) {
|
||||
if (++stuckFrames >= STUCK_FRAME_THRESHOLD) {
|
||||
mCancelled = true;
|
||||
}
|
||||
@@ -305,6 +312,8 @@ public final class MediaConverter {
|
||||
}
|
||||
|
||||
// TODO: Check the generated output file.
|
||||
|
||||
return muxing;
|
||||
}
|
||||
|
||||
static String getMimeTypeFor(MediaFormat format) {
|
||||
|
||||
@@ -13,6 +13,7 @@ import androidx.annotation.RequiresApi;
|
||||
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.thoughtcrime.securesms.video.interfaces.MediaInput;
|
||||
import org.thoughtcrime.securesms.video.videoconverter.utils.MediaCodecCompat;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
@@ -88,19 +89,25 @@ final class VideoThumbnailsExtractor {
|
||||
outputSurface = new OutputSurface(outputWidthRotated, outputHeightRotated, true);
|
||||
|
||||
decoder = MediaCodec.createDecoderByType(mime);
|
||||
if (Build.VERSION.SDK_INT >= 31) {
|
||||
final String VENDOR_DOLBY_CODEC_TRANSFER_PARAMKEY = "vendor.dolby.codec.transfer.value";
|
||||
MediaCodec.ParameterDescriptor descriptor = decoder.getParameterDescriptor(VENDOR_DOLBY_CODEC_TRANSFER_PARAMKEY);
|
||||
if (descriptor != null) {
|
||||
Bundle transferBundle = new Bundle();
|
||||
transferBundle.putString(VENDOR_DOLBY_CODEC_TRANSFER_PARAMKEY, "transfer.sdr.normal");
|
||||
decoder.setParameters(transferBundle);
|
||||
} else {
|
||||
mediaFormat.setInteger(MediaFormat.KEY_COLOR_TRANSFER_REQUEST, MediaFormat.COLOR_TRANSFER_SDR_VIDEO);
|
||||
}
|
||||
final boolean isHdr = MediaCodecCompat.isHdrVideo(mediaFormat);
|
||||
if (Build.VERSION.SDK_INT >= 31 && isHdr) {
|
||||
mediaFormat.setInteger(MediaFormat.KEY_COLOR_TRANSFER_REQUEST, MediaFormat.COLOR_TRANSFER_SDR_VIDEO);
|
||||
}
|
||||
decoder.configure(mediaFormat, outputSurface.getSurface(), null, 0);
|
||||
decoder.start();
|
||||
if (Build.VERSION.SDK_INT >= 31 && isHdr) {
|
||||
try {
|
||||
final String VENDOR_DOLBY_CODEC_TRANSFER_PARAMKEY = "vendor.dolby.codec.transfer.value";
|
||||
MediaCodec.ParameterDescriptor descriptor = decoder.getParameterDescriptor(VENDOR_DOLBY_CODEC_TRANSFER_PARAMKEY);
|
||||
if (descriptor != null) {
|
||||
Bundle transferBundle = new Bundle();
|
||||
transferBundle.putString(VENDOR_DOLBY_CODEC_TRANSFER_PARAMKEY, "transfer.sdr.normal");
|
||||
decoder.setParameters(transferBundle);
|
||||
}
|
||||
} catch (IllegalStateException e) {
|
||||
Log.w(TAG, "Failed to set Dolby Vision transfer parameter", e);
|
||||
}
|
||||
}
|
||||
|
||||
long duration = 0;
|
||||
|
||||
@@ -217,4 +224,5 @@ final class VideoThumbnailsExtractor {
|
||||
}
|
||||
Log.i(TAG, "doExtract finished");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -150,11 +150,6 @@ final class VideoTrackConverter {
|
||||
outputHeightRotated = outputHeight;
|
||||
}
|
||||
|
||||
final ColorInfo colorInfo = preDecodeColorInfo(mVideoExtractor, inputVideoFormat);
|
||||
// IMPORTANT: reset extractor after probing
|
||||
mVideoExtractor.unselectTrack(videoInputTrack);
|
||||
mVideoExtractor.selectTrack(videoInputTrack);
|
||||
mVideoExtractor.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
|
||||
final MediaFormat outputVideoFormat = MediaFormat.createVideoFormat(videoCodec, outputWidthRotated, outputHeightRotated);
|
||||
|
||||
// Set some properties. Failing to specify some of these can cause the MediaCodec
|
||||
@@ -192,82 +187,6 @@ final class VideoTrackConverter {
|
||||
}
|
||||
}
|
||||
|
||||
private ColorInfo preDecodeColorInfo(MediaExtractor extractor, MediaFormat inputFormat) throws IOException {
|
||||
MediaCodec decoder = MediaCodec.createDecoderByType(inputFormat.getString(MediaFormat.KEY_MIME));
|
||||
decoder.configure(inputFormat, null, null, 0);
|
||||
decoder.start();
|
||||
|
||||
MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
|
||||
boolean outputFormatKnown = false;
|
||||
Integer colorStandard = null, colorTransfer = null, colorRange = null;
|
||||
|
||||
boolean inputDone = false;
|
||||
|
||||
while (!outputFormatKnown) {
|
||||
|
||||
// ---- FEED INPUT ----
|
||||
if (!inputDone) {
|
||||
int inIndex = decoder.dequeueInputBuffer(20000);
|
||||
if (inIndex >= 0) {
|
||||
ByteBuffer inputBuffer = decoder.getInputBuffer(inIndex);
|
||||
int sampleSize = extractor.readSampleData(inputBuffer, 0);
|
||||
|
||||
if (sampleSize < 0) {
|
||||
decoder.queueInputBuffer(
|
||||
inIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM
|
||||
);
|
||||
inputDone = true;
|
||||
} else {
|
||||
long pts = extractor.getSampleTime();
|
||||
decoder.queueInputBuffer(inIndex, 0, sampleSize, pts, 0);
|
||||
extractor.advance();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---- DRAIN OUTPUT ----
|
||||
int outIndex = decoder.dequeueOutputBuffer(info, 20000);
|
||||
|
||||
if (outIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
|
||||
MediaFormat fmt = decoder.getOutputFormat();
|
||||
if (fmt.containsKey(MediaFormat.KEY_COLOR_STANDARD))
|
||||
colorStandard = fmt.getInteger(MediaFormat.KEY_COLOR_STANDARD);
|
||||
if (fmt.containsKey(MediaFormat.KEY_COLOR_TRANSFER))
|
||||
colorTransfer = fmt.getInteger(MediaFormat.KEY_COLOR_TRANSFER);
|
||||
if (fmt.containsKey(MediaFormat.KEY_COLOR_RANGE))
|
||||
colorRange = fmt.getInteger(MediaFormat.KEY_COLOR_RANGE);
|
||||
outputFormatKnown = true;
|
||||
} else if (outIndex >= 0) {
|
||||
// We won't render, but must release output buffers
|
||||
decoder.releaseOutputBuffer(outIndex, false);
|
||||
}
|
||||
|
||||
// If EOS reached and still no format → decoder doesn’t provide it
|
||||
if (inputDone && outIndex == MediaCodec.INFO_TRY_AGAIN_LATER) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
decoder.stop();
|
||||
decoder.release();
|
||||
|
||||
return new ColorInfo(colorStandard, colorTransfer, colorRange);
|
||||
}
|
||||
|
||||
|
||||
private boolean isHdr(MediaFormat inputVideoFormat) {
|
||||
if (Build.VERSION.SDK_INT < 24) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
final int colorInfo = inputVideoFormat.getInteger(MediaFormat.KEY_COLOR_TRANSFER);
|
||||
return colorInfo == MediaFormat.COLOR_TRANSFER_ST2084 || colorInfo == MediaFormat.COLOR_TRANSFER_HLG;
|
||||
} catch (NullPointerException npe) {
|
||||
// color transfer key does not exist, no color data supplied
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void setMuxer(final @NonNull Muxer muxer) throws IOException {
|
||||
mMuxer = muxer;
|
||||
if (mEncoderOutputVideoFormat != null) {
|
||||
@@ -562,20 +481,27 @@ final class VideoTrackConverter {
|
||||
final Pair<MediaCodec, MediaFormat> decoderPair = MediaCodecCompat.findDecoder(inputFormat);
|
||||
final MediaCodec decoder = decoderPair.getFirst();
|
||||
|
||||
// Try to use the Dolby Vision decoder, but if it doesn't support the transfer parameter, the decoded video buffer
|
||||
// is HLG and in-app tone mapping has to be used instead
|
||||
if (Build.VERSION.SDK_INT >= 31) {
|
||||
MediaCodec.ParameterDescriptor descriptor = decoder.getParameterDescriptor(VENDOR_DOLBY_CODEC_TRANSFER_PARAMKEY);
|
||||
if (descriptor != null) {
|
||||
Bundle transferBundle = new Bundle();
|
||||
transferBundle.putString(VENDOR_DOLBY_CODEC_TRANSFER_PARAMKEY, "transfer.sdr.normal");
|
||||
decoder.setParameters(transferBundle);
|
||||
} else {
|
||||
decoderPair.getSecond().setInteger(MediaFormat.KEY_COLOR_TRANSFER_REQUEST, MediaFormat.COLOR_TRANSFER_SDR_VIDEO);
|
||||
}
|
||||
// For HDR video, request SDR tone-mapping from the decoder. Only do this for HDR content
|
||||
// (PQ or HLG transfer), as some hardware decoders (e.g. Qualcomm HEVC) crash when this is
|
||||
// set on non-HDR video.
|
||||
final boolean isHdr = MediaCodecCompat.isHdrVideo(decoderPair.getSecond());
|
||||
if (Build.VERSION.SDK_INT >= 31 && isHdr) {
|
||||
decoderPair.getSecond().setInteger(MediaFormat.KEY_COLOR_TRANSFER_REQUEST, MediaFormat.COLOR_TRANSFER_SDR_VIDEO);
|
||||
}
|
||||
decoder.configure(decoderPair.getSecond(), surface, null, 0);
|
||||
decoder.start();
|
||||
if (Build.VERSION.SDK_INT >= 31 && isHdr) {
|
||||
try {
|
||||
MediaCodec.ParameterDescriptor descriptor = decoder.getParameterDescriptor(VENDOR_DOLBY_CODEC_TRANSFER_PARAMKEY);
|
||||
if (descriptor != null) {
|
||||
Bundle transferBundle = new Bundle();
|
||||
transferBundle.putString(VENDOR_DOLBY_CODEC_TRANSFER_PARAMKEY, "transfer.sdr.normal");
|
||||
decoder.setParameters(transferBundle);
|
||||
}
|
||||
} catch (IllegalStateException e) {
|
||||
Log.w(TAG, "Failed to set Dolby Vision transfer parameter", e);
|
||||
}
|
||||
}
|
||||
return decoder;
|
||||
}
|
||||
|
||||
@@ -626,16 +552,5 @@ final class VideoTrackConverter {
|
||||
return MediaConverter.getMimeTypeFor(format).startsWith("video/");
|
||||
}
|
||||
|
||||
private class ColorInfo {
|
||||
public final Integer colorStandard;
|
||||
public final Integer colorTransfer;
|
||||
public final Integer colorRange;
|
||||
|
||||
public ColorInfo(Integer colorSpace, Integer transfer, Integer primaries) {
|
||||
this.colorStandard = colorSpace;
|
||||
this.colorTransfer = transfer;
|
||||
this.colorRange = primaries;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -149,4 +149,20 @@ object MediaCodecCompat {
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given [MediaFormat] describes an HDR video (PQ or HLG color transfer).
|
||||
* Some hardware decoders crash when tone-mapping parameters are set on non-HDR video.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun isHdrVideo(format: MediaFormat): Boolean {
|
||||
return try {
|
||||
val colorTransfer = format.getInteger(MediaFormat.KEY_COLOR_TRANSFER)
|
||||
colorTransfer == MediaFormat.COLOR_TRANSFER_ST2084 || colorTransfer == MediaFormat.COLOR_TRANSFER_HLG
|
||||
} catch (e: NullPointerException) {
|
||||
false
|
||||
} catch (e: ClassCastException) {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user