From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- .../main/java/org/mozilla/gecko/media/Sample.java | 291 +++++++++++++++++++++ 1 file changed, 291 insertions(+) create mode 100644 mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/Sample.java (limited to 'mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/Sample.java') diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/Sample.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/Sample.java new file mode 100644 index 0000000000..baa6737427 --- /dev/null +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/Sample.java @@ -0,0 +1,291 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.gecko.media; + +import android.annotation.SuppressLint; +import android.media.MediaCodec; +import android.media.MediaCodec.BufferInfo; +import android.media.MediaCodec.CryptoInfo; +import android.os.Build; +import android.os.Parcel; +import android.os.Parcelable; +import androidx.annotation.ChecksSdkIntAtLeast; +import java.lang.reflect.Field; +import java.nio.ByteBuffer; +import org.mozilla.gecko.annotation.WrapForJNI; + +// Parcelable carrying input/output sample data and info cross process. +public final class Sample implements Parcelable { + public static final Sample EOS; + + static { + final BufferInfo eosInfo = new BufferInfo(); + EOS = new Sample(); + EOS.info.set(0, 0, Long.MIN_VALUE, MediaCodec.BUFFER_FLAG_END_OF_STREAM); + } + + @WrapForJNI public long session; + + public static final int NO_BUFFER = -1; + + public int bufferId = NO_BUFFER; + @WrapForJNI public BufferInfo info = new BufferInfo(); + public CryptoInfo cryptoInfo; + + // Simple Linked list for recycling objects. + // Used to nodify Sample objects. Do not marshal/unmarshal. + private Sample mNext; + private static Sample sPool = new Sample(); + private static int sPoolSize = 1; + + private Sample() {} + + private void readInfo(final Parcel in) { + final int offset = in.readInt(); + final int size = in.readInt(); + final long pts = in.readLong(); + final int flags = in.readInt(); + + info.set(offset, size, pts, flags); + } + + private void readCrypto(final Parcel in) { + final int hasCryptoInfo = in.readInt(); + if (hasCryptoInfo == 0) { + cryptoInfo = null; + return; + } + + final byte[] iv = in.createByteArray(); + final byte[] key = in.createByteArray(); + final int mode = in.readInt(); + final int[] numBytesOfClearData = in.createIntArray(); + final int[] numBytesOfEncryptedData = in.createIntArray(); + final int numSubSamples = in.readInt(); + + if (cryptoInfo == null) { + cryptoInfo = new CryptoInfo(); + } + cryptoInfo.set(numSubSamples, numBytesOfClearData, numBytesOfEncryptedData, key, iv, mode); + if (supportsCryptoPattern()) { + final int numEncryptBlocks = in.readInt(); + final int numSkipBlocks = in.readInt(); + cryptoInfo.setPattern(new CryptoInfo.Pattern(numEncryptBlocks, numSkipBlocks)); + } + } + + public Sample set(final BufferInfo info, final CryptoInfo cryptoInfo) { + setBufferInfo(info); + setCryptoInfo(cryptoInfo); + return this; + } + + public void setBufferInfo(final BufferInfo info) { + this.info.set(0, info.size, info.presentationTimeUs, info.flags); + } + + public void setCryptoInfo(final CryptoInfo crypto) { + if (crypto == null) { + cryptoInfo = null; + return; + } + + if (cryptoInfo == null) { + cryptoInfo = new CryptoInfo(); + } + cryptoInfo.set( + crypto.numSubSamples, + crypto.numBytesOfClearData, + crypto.numBytesOfEncryptedData, + crypto.key, + crypto.iv, + crypto.mode); + if (supportsCryptoPattern()) { + final CryptoInfo.Pattern pattern = getCryptoPatternCompat(crypto); + if (pattern == null) { + return; + } + cryptoInfo.setPattern(pattern); + } + } + + @WrapForJNI + public void dispose() { + if (isEOS()) { + return; + } + + bufferId = NO_BUFFER; + info.set(0, 0, 0, 0); + if (cryptoInfo != null) { + cryptoInfo.set(0, null, null, null, null, 0); + } + + // Recycle it. + synchronized (CREATOR) { + this.mNext = sPool; + sPool = this; + sPoolSize++; + } + } + + public boolean isEOS() { + return (this == EOS) || ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0); + } + + public static Sample obtain() { + synchronized (CREATOR) { + Sample s = null; + if (sPoolSize > 0) { + s = sPool; + sPool = s.mNext; + s.mNext = null; + sPoolSize--; + } else { + s = new Sample(); + } + return s; + } + } + + public static final Creator CREATOR = + new Creator() { + @Override + public Sample createFromParcel(final Parcel in) { + return obtainSample(in); + } + + @Override + public Sample[] newArray(final int size) { + return new Sample[size]; + } + + private Sample obtainSample(final Parcel in) { + final Sample s = obtain(); + s.session = in.readLong(); + s.bufferId = in.readInt(); + s.readInfo(in); + s.readCrypto(in); + return s; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(final Parcel dest, final int parcelableFlags) { + dest.writeLong(session); + dest.writeInt(bufferId); + writeInfo(dest); + writeCrypto(dest); + } + + private void writeInfo(final Parcel dest) { + dest.writeInt(info.offset); + dest.writeInt(info.size); + dest.writeLong(info.presentationTimeUs); + dest.writeInt(info.flags); + } + + private void writeCrypto(final Parcel dest) { + if (cryptoInfo != null) { + dest.writeInt(1); + dest.writeByteArray(cryptoInfo.iv); + dest.writeByteArray(cryptoInfo.key); + dest.writeInt(cryptoInfo.mode); + dest.writeIntArray(cryptoInfo.numBytesOfClearData); + dest.writeIntArray(cryptoInfo.numBytesOfEncryptedData); + dest.writeInt(cryptoInfo.numSubSamples); + if (supportsCryptoPattern()) { + final CryptoInfo.Pattern pattern = getCryptoPatternCompat(cryptoInfo); + if (pattern != null) { + dest.writeInt(pattern.getEncryptBlocks()); + dest.writeInt(pattern.getSkipBlocks()); + } else { + // Couldn't get pattern - write default values + dest.writeInt(0); + dest.writeInt(0); + } + } + } else { + dest.writeInt(0); + } + } + + public static byte[] byteArrayFromBuffer( + final ByteBuffer buffer, final int offset, final int size) { + if (buffer == null || buffer.capacity() == 0 || size == 0) { + return null; + } + if (buffer.hasArray() && offset == 0 && buffer.array().length == size) { + return buffer.array(); + } + final int length = Math.min(offset + size, buffer.capacity()) - offset; + final byte[] bytes = new byte[length]; + buffer.position(offset); + buffer.get(bytes); + return bytes; + } + + @Override + public String toString() { + if (isEOS()) { + return "EOS sample"; + } + + final StringBuilder str = new StringBuilder(); + str.append("{ session#:") + .append(session) + .append(", buffer#") + .append(bufferId) + .append(", info=") + .append("{ offset=") + .append(info.offset) + .append(", size=") + .append(info.size) + .append(", pts=") + .append(info.presentationTimeUs) + .append(", flags=") + .append(Integer.toHexString(info.flags)) + .append(" }") + .append(" }"); + return str.toString(); + } + + @ChecksSdkIntAtLeast(api = android.os.Build.VERSION_CODES.N) + public static boolean supportsCryptoPattern() { + return Build.VERSION.SDK_INT >= 24; + } + + @SuppressLint("DiscouragedPrivateApi") + public static CryptoInfo.Pattern getCryptoPatternCompat(final CryptoInfo cryptoInfo) { + if (!supportsCryptoPattern()) { + return null; + } + // getPattern() added in API 31: + // https://developer.android.com/reference/android/media/MediaCodec.CryptoInfo#getPattern() + if (Build.VERSION.SDK_INT >= 31) { + return cryptoInfo.getPattern(); + } + + // CryptoInfo.Pattern added in API 24: + // https://developer.android.com/reference/android/media/MediaCodec.CryptoInfo.Pattern + if (Build.VERSION.SDK_INT >= 24) { + try { + // Without getPattern(), no way to access the pattern without reflection. + // https://cs.android.com/android/platform/superproject/+/android-11.0.0_r1:frameworks/base/media/java/android/media/MediaCodec.java;l=2718;drc=3c715d5778e15dc84082e63dc65b382d31fe8e45 + final Field patternField = CryptoInfo.class.getDeclaredField("pattern"); + patternField.setAccessible(true); + return (CryptoInfo.Pattern) patternField.get(cryptoInfo); + } catch (final NoSuchFieldException | IllegalAccessException e) { + return null; + } + } + return null; + } +} -- cgit v1.2.3