mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-17 07:23:21 +01:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2e0e8c2235 | ||
|
|
5c6962082a | ||
|
|
701bed970b | ||
|
|
d0918dcb7b | ||
|
|
36aae9823b | ||
|
|
b1f5206680 | ||
|
|
38ed75fb64 | ||
|
|
26ab20f860 |
@@ -1,17 +1,7 @@
|
|||||||
@file:Suppress("UnstableApiUsage")
|
@file:Suppress("UnstableApiUsage")
|
||||||
|
|
||||||
import com.android.build.api.dsl.ManagedVirtualDevice
|
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 org.gradle.api.tasks.testing.logging.TestExceptionFormat
|
||||||
import java.io.File
|
|
||||||
import java.util.Properties
|
import java.util.Properties
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
@@ -31,8 +21,8 @@ plugins {
|
|||||||
apply(from = "static-ips.gradle.kts")
|
apply(from = "static-ips.gradle.kts")
|
||||||
|
|
||||||
val canonicalVersionCode = 1651
|
val canonicalVersionCode = 1651
|
||||||
val canonicalVersionName = "7.74.3"
|
val canonicalVersionName = "7.74.5"
|
||||||
val currentHotfixVersion = 0
|
val currentHotfixVersion = 2
|
||||||
val maxHotfixVersions = 100
|
val maxHotfixVersions = 100
|
||||||
|
|
||||||
// We don't want versions to ever end in 0 so that they don't conflict with nightly versions
|
// 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)
|
int mmsSubscriptionId)
|
||||||
{
|
{
|
||||||
return new AttachmentCompressionJob(databaseAttachment.attachmentId,
|
return new AttachmentCompressionJob(databaseAttachment.attachmentId,
|
||||||
MediaUtil.isVideo(databaseAttachment) && MediaConstraints.isVideoTranscodeAvailable(),
|
MediaUtil.isVideo(databaseAttachment) && !databaseAttachment.videoGif && MediaConstraints.isVideoTranscodeAvailable(),
|
||||||
mms,
|
mms,
|
||||||
mmsSubscriptionId);
|
mmsSubscriptionId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -458,8 +458,8 @@ class RegistrationViewModel : ViewModel() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun submitCaptchaToken(context: Context) {
|
fun submitCaptchaToken(context: Context) {
|
||||||
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 captcha token.") }
|
||||||
val captchaToken = store.value.captchaToken ?: throw IllegalStateException("Can't submit captcha token if no captcha token is set!")
|
val captchaToken = store.value.captchaToken ?: return bail { Log.w(TAG, "Captcha token was null when trying to submit captcha token.") }
|
||||||
|
|
||||||
store.update {
|
store.update {
|
||||||
it.copy(captchaToken = null, challengeInProgress = true, inProgress = true)
|
it.copy(captchaToken = null, challengeInProgress = true, inProgress = true)
|
||||||
@@ -486,7 +486,7 @@ class RegistrationViewModel : ViewModel() {
|
|||||||
fun requestAndSubmitPushToken(context: Context) {
|
fun requestAndSubmitPushToken(context: Context) {
|
||||||
Log.v(TAG, "validatePushToken()")
|
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 {
|
viewModelScope.launch {
|
||||||
Log.d(TAG, "Getting session in order to perform push token verification…")
|
Log.d(TAG, "Getting session in order to perform push token verification…")
|
||||||
@@ -1063,6 +1063,22 @@ class RegistrationViewModel : ViewModel() {
|
|||||||
setInProgress(false)
|
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?) {
|
fun registerWithBackupKey(context: Context, backupKey: String, e164: String?, pin: String?, aciIdentityKeyPair: IdentityKeyPair?, pniIdentityKeyPair: IdentityKeyPair?) {
|
||||||
setInProgress(true)
|
setInProgress(true)
|
||||||
|
|
||||||
|
|||||||
@@ -600,6 +600,7 @@ class EnterPhoneNumberFragment : LoggingFragment(R.layout.fragment_registration_
|
|||||||
private fun updateEnabledControls(showProgress: Boolean, isReRegister: Boolean) {
|
private fun updateEnabledControls(showProgress: Boolean, isReRegister: Boolean) {
|
||||||
binding.countryCode.isEnabled = !showProgress
|
binding.countryCode.isEnabled = !showProgress
|
||||||
binding.number.isEnabled = !showProgress
|
binding.number.isEnabled = !showProgress
|
||||||
|
countryPickerView.isEnabled = !showProgress
|
||||||
binding.cancelButton.visible = !showProgress && isReRegister
|
binding.cancelButton.visible = !showProgress && isReRegister
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -150,6 +150,7 @@ public final class MediaConverter {
|
|||||||
AudioTrackConverter audioTrackConverter = null;
|
AudioTrackConverter audioTrackConverter = null;
|
||||||
|
|
||||||
long mdatContentLength = 0;
|
long mdatContentLength = 0;
|
||||||
|
boolean muxerStarted = false;
|
||||||
boolean muxerStopped = false;
|
boolean muxerStopped = false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -162,13 +163,17 @@ public final class MediaConverter {
|
|||||||
throw new EncodingException("No video and audio tracks");
|
throw new EncodingException("No video and audio tracks");
|
||||||
}
|
}
|
||||||
|
|
||||||
doExtractDecodeEditEncodeMux(
|
muxerStarted = doExtractDecodeEditEncodeMux(
|
||||||
videoTrackConverter,
|
videoTrackConverter,
|
||||||
audioTrackConverter,
|
audioTrackConverter,
|
||||||
muxer);
|
muxer);
|
||||||
|
|
||||||
mdatContentLength = muxer.stop();
|
if (muxerStarted) {
|
||||||
muxerStopped = true;
|
mdatContentLength = muxer.stop();
|
||||||
|
muxerStopped = true;
|
||||||
|
} else if (mCancelled) {
|
||||||
|
throw new EncodingException("Conversion cancelled before muxing started");
|
||||||
|
}
|
||||||
|
|
||||||
} catch (EncodingException | IOException e) {
|
} catch (EncodingException | IOException e) {
|
||||||
Log.e(TAG, "error converting", e);
|
Log.e(TAG, "error converting", e);
|
||||||
@@ -204,7 +209,7 @@ public final class MediaConverter {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
if (muxer != null) {
|
if (muxer != null) {
|
||||||
if (!muxerStopped) {
|
if (!muxerStopped && muxerStarted) {
|
||||||
muxer.stop();
|
muxer.stop();
|
||||||
}
|
}
|
||||||
muxer.release();
|
muxer.release();
|
||||||
@@ -225,8 +230,10 @@ public final class MediaConverter {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Does the actual work for extracting, decoding, encoding and muxing.
|
* 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 VideoTrackConverter videoTrackConverter,
|
||||||
final @Nullable AudioTrackConverter audioTrackConverter,
|
final @Nullable AudioTrackConverter audioTrackConverter,
|
||||||
final @NonNull Muxer muxer) throws IOException, TranscodingException {
|
final @NonNull Muxer muxer) throws IOException, TranscodingException {
|
||||||
@@ -249,7 +256,7 @@ public final class MediaConverter {
|
|||||||
Log.d(TAG, "loop: " + currentState);
|
Log.d(TAG, "loop: " + currentState);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentState.equals(oldState)) {
|
if (muxing && currentState.equals(oldState)) {
|
||||||
if (++stuckFrames >= STUCK_FRAME_THRESHOLD) {
|
if (++stuckFrames >= STUCK_FRAME_THRESHOLD) {
|
||||||
mCancelled = true;
|
mCancelled = true;
|
||||||
}
|
}
|
||||||
@@ -305,6 +312,8 @@ public final class MediaConverter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Check the generated output file.
|
// TODO: Check the generated output file.
|
||||||
|
|
||||||
|
return muxing;
|
||||||
}
|
}
|
||||||
|
|
||||||
static String getMimeTypeFor(MediaFormat format) {
|
static String getMimeTypeFor(MediaFormat format) {
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import androidx.annotation.RequiresApi;
|
|||||||
|
|
||||||
import org.signal.core.util.logging.Log;
|
import org.signal.core.util.logging.Log;
|
||||||
import org.thoughtcrime.securesms.video.interfaces.MediaInput;
|
import org.thoughtcrime.securesms.video.interfaces.MediaInput;
|
||||||
|
import org.thoughtcrime.securesms.video.videoconverter.utils.MediaCodecCompat;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.ByteOrder;
|
import java.nio.ByteOrder;
|
||||||
@@ -88,19 +89,25 @@ final class VideoThumbnailsExtractor {
|
|||||||
outputSurface = new OutputSurface(outputWidthRotated, outputHeightRotated, true);
|
outputSurface = new OutputSurface(outputWidthRotated, outputHeightRotated, true);
|
||||||
|
|
||||||
decoder = MediaCodec.createDecoderByType(mime);
|
decoder = MediaCodec.createDecoderByType(mime);
|
||||||
if (Build.VERSION.SDK_INT >= 31) {
|
final boolean isHdr = MediaCodecCompat.isHdrVideo(mediaFormat);
|
||||||
final String VENDOR_DOLBY_CODEC_TRANSFER_PARAMKEY = "vendor.dolby.codec.transfer.value";
|
if (Build.VERSION.SDK_INT >= 31 && isHdr) {
|
||||||
MediaCodec.ParameterDescriptor descriptor = decoder.getParameterDescriptor(VENDOR_DOLBY_CODEC_TRANSFER_PARAMKEY);
|
mediaFormat.setInteger(MediaFormat.KEY_COLOR_TRANSFER_REQUEST, MediaFormat.COLOR_TRANSFER_SDR_VIDEO);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
decoder.configure(mediaFormat, outputSurface.getSurface(), null, 0);
|
decoder.configure(mediaFormat, outputSurface.getSurface(), null, 0);
|
||||||
decoder.start();
|
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;
|
long duration = 0;
|
||||||
|
|
||||||
@@ -217,4 +224,5 @@ final class VideoThumbnailsExtractor {
|
|||||||
}
|
}
|
||||||
Log.i(TAG, "doExtract finished");
|
Log.i(TAG, "doExtract finished");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -150,11 +150,6 @@ final class VideoTrackConverter {
|
|||||||
outputHeightRotated = outputHeight;
|
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);
|
final MediaFormat outputVideoFormat = MediaFormat.createVideoFormat(videoCodec, outputWidthRotated, outputHeightRotated);
|
||||||
|
|
||||||
// Set some properties. Failing to specify some of these can cause the MediaCodec
|
// 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 {
|
void setMuxer(final @NonNull Muxer muxer) throws IOException {
|
||||||
mMuxer = muxer;
|
mMuxer = muxer;
|
||||||
if (mEncoderOutputVideoFormat != null) {
|
if (mEncoderOutputVideoFormat != null) {
|
||||||
@@ -562,20 +481,27 @@ final class VideoTrackConverter {
|
|||||||
final Pair<MediaCodec, MediaFormat> decoderPair = MediaCodecCompat.findDecoder(inputFormat);
|
final Pair<MediaCodec, MediaFormat> decoderPair = MediaCodecCompat.findDecoder(inputFormat);
|
||||||
final MediaCodec decoder = decoderPair.getFirst();
|
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
|
// For HDR video, request SDR tone-mapping from the decoder. Only do this for HDR content
|
||||||
// is HLG and in-app tone mapping has to be used instead
|
// (PQ or HLG transfer), as some hardware decoders (e.g. Qualcomm HEVC) crash when this is
|
||||||
if (Build.VERSION.SDK_INT >= 31) {
|
// set on non-HDR video.
|
||||||
MediaCodec.ParameterDescriptor descriptor = decoder.getParameterDescriptor(VENDOR_DOLBY_CODEC_TRANSFER_PARAMKEY);
|
final boolean isHdr = MediaCodecCompat.isHdrVideo(decoderPair.getSecond());
|
||||||
if (descriptor != null) {
|
if (Build.VERSION.SDK_INT >= 31 && isHdr) {
|
||||||
Bundle transferBundle = new Bundle();
|
decoderPair.getSecond().setInteger(MediaFormat.KEY_COLOR_TRANSFER_REQUEST, MediaFormat.COLOR_TRANSFER_SDR_VIDEO);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
decoder.configure(decoderPair.getSecond(), surface, null, 0);
|
decoder.configure(decoderPair.getSecond(), surface, null, 0);
|
||||||
decoder.start();
|
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;
|
return decoder;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -626,16 +552,5 @@ final class VideoTrackConverter {
|
|||||||
return MediaConverter.getMimeTypeFor(format).startsWith("video/");
|
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 {
|
} else {
|
||||||
null
|
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