--- a/dom/media/platforms/android/MediaCodecDataDecoder.cpp
+++ b/dom/media/platforms/android/MediaCodecDataDecoder.cpp
@@ -146,30 +146,44 @@ public:
: VideoDataDecoder(aConfig, aFormat, aCallback, aImageContainer, aDrmStubId)
, mSamplesWaitingForKey(new SamplesWaitingForKey(this, aCallback,
aTaskQueue, aProxy))
{
}
void Input(MediaRawData* aSample) override;
void Shutdown() override;
+ bool WaitForKeys() override;
private:
RefPtr<SamplesWaitingForKey> mSamplesWaitingForKey;
};
void
EMEVideoDataDecoder::Input(MediaRawData* aSample)
{
- if (mSamplesWaitingForKey->WaitIfKeyNotUsable(aSample)) {
+ // For Android L or older, we cannot receive meaningful keyId from CDM.
+ // So decoder's not going to wait for certain keyId until the key becomes usable.
+ // Instead, decoder is decoding directly and if no-key exception happens, we'll
+ // retry later when the key may be usable.
+ if (!MediaDrmProxy::IsPreMarshmallow() && mSamplesWaitingForKey->WaitIfKeyNotUsable(aSample)) {
return;
}
+
VideoDataDecoder::Input(aSample);
}
+bool
+EMEVideoDataDecoder::WaitForKeys()
+{
+ return MediaDrmProxy::IsPreMarshmallow() ?
+ MediaDrmProxy::WaitForKey(mDrmStubId) :
+ false;
+}
+
void
EMEVideoDataDecoder::Shutdown()
{
VideoDataDecoder::Shutdown();
mSamplesWaitingForKey->BreakCycles();
mSamplesWaitingForKey = nullptr;
}
@@ -264,30 +278,44 @@ public:
: AudioDataDecoder(aConfig, aFormat, aCallback, aDrmStubId)
, mSamplesWaitingForKey(new SamplesWaitingForKey(this, aCallback,
aTaskQueue, aProxy))
{
}
void Input(MediaRawData* aSample) override;
void Shutdown() override;
+ bool WaitForKeys() override;
private:
RefPtr<SamplesWaitingForKey> mSamplesWaitingForKey;
};
void
EMEAudioDataDecoder::Input(MediaRawData* aSample)
{
- if (mSamplesWaitingForKey->WaitIfKeyNotUsable(aSample)) {
+ // For Android L or older, we cannot receive meaningful keyId from CDM.
+ // So decoder's not going to wait for certain keyId until the key becomes usable.
+ // Instead, decoder is decoding directly and if no-key exception happens, we'll
+ // retry later when the key may be usable.
+ if (!MediaDrmProxy::IsPreMarshmallow() && mSamplesWaitingForKey->WaitIfKeyNotUsable(aSample)) {
return;
}
+
AudioDataDecoder::Input(aSample);
}
+bool
+EMEAudioDataDecoder::WaitForKeys()
+{
+ return MediaDrmProxy::IsPreMarshmallow() ?
+ MediaDrmProxy::WaitForKey(mDrmStubId) :
+ false;
+}
+
void
EMEAudioDataDecoder::Shutdown()
{
AudioDataDecoder::Shutdown();
mSamplesWaitingForKey->BreakCycles();
mSamplesWaitingForKey = nullptr;
}
@@ -481,16 +509,43 @@ MediaCodecDataDecoder::PeekNextSample()
}
return nullptr;
}
// We're not stopping or flushing, so try to get a sample.
return RefPtr<MediaRawData>(mQueue.front()).forget();
}
+static nsresult
+QueueSecureInputBufferHelper(MediaCodec::LocalRef aDecoder,
+ const nsString& aDrmStubId,
+ int32_t aInputIdx, int32_t aOffset,
+ CryptoInfo::LocalRef aCryptoInfo, int64_t aPTS,
+ int32_t aFlags)
+{
+ int status = MediaCodecUtil::CODEC_STATUS_OK;
+ if (MediaDrmProxy::IsPreMarshmallow()) {
+ status = MediaCodecUtil::QueueSecureInputBufferV21(aDecoder,
+ aDrmStubId,
+ aInputIdx,
+ aOffset,
+ aCryptoInfo,
+ aPTS,
+ aFlags);
+ } else {
+ status = MediaCodecUtil::QueueSecureInputBufferV23(aDecoder,
+ aInputIdx,
+ aOffset,
+ aCryptoInfo,
+ aPTS,
+ aFlags);
+ }
+ return status == MediaCodecUtil::CODEC_STATUS_OK ? NS_OK : NS_ERROR_FAILURE;
+}
+
nsresult
MediaCodecDataDecoder::QueueSample(const MediaRawData* aSample)
{
MOZ_ASSERT(aSample);
AutoLocalJNIFrame frame(jni::GetEnvForThread(), 1);
// We have a sample, try to feed it to the decoder.
int32_t inputIndex = -1;
@@ -515,18 +570,19 @@ MediaCodecDataDecoder::QueueSample(const
MOZ_ASSERT(frame.GetEnv()->GetDirectBufferCapacity(buffer.Get()) >=
aSample->Size(),
"Decoder buffer is not large enough for sample");
PodCopy(static_cast<uint8_t*>(directBuffer), aSample->Data(), aSample->Size());
CryptoInfo::LocalRef cryptoInfo = GetCryptoInfoFromSample(aSample);
if (cryptoInfo) {
- res = mDecoder->QueueSecureInputBuffer(inputIndex, 0, cryptoInfo,
- aSample->mTime, 0);
+ res = QueueSecureInputBufferHelper(mDecoder, mDrmStubId,
+ inputIndex, 0, cryptoInfo,
+ aSample->mTime, 0);
} else {
res = mDecoder->QueueInputBuffer(inputIndex, 0, aSample->Size(),
aSample->mTime, 0);
}
if (NS_FAILED(res)) {
return res;
}
@@ -627,24 +683,35 @@ MediaCodecDataDecoder::DecoderLoop()
MonitorAutoLock lock(mMonitor);
if (mState == ModuleState::kDrainDecoder) {
MOZ_ASSERT(!sample, "Shouldn't have a sample when pushing EOF frame");
res = QueueEOS();
BREAK_ON_DECODER_ERROR();
}
}
+ // Both Audio/Video are blocked even if only one of them needs a keys.
+ if (WaitForKeys()) {
+ // Loop will be blocked until new keys are updated.
+ MediaDrmProxy::DoWait(mDrmStubId);
+ continue;
+ }
+
if (sample) {
res = QueueSample(sample);
if (NS_SUCCEEDED(res)) {
// We've fed this into the decoder, so remove it from the queue.
MonitorAutoLock lock(mMonitor);
MOZ_RELEASE_ASSERT(mQueue.size(), "Queue may not be empty");
mQueue.pop_front();
isOutputDone = false;
+ } else {
+ if (WaitForKeys()) {
+ continue;
+ }
}
}
if (isOutputDone) {
continue;
}
BufferInfo::LocalRef bufferInfo;
--- a/dom/media/platforms/android/MediaCodecDataDecoder.h
+++ b/dom/media/platforms/android/MediaCodecDataDecoder.h
@@ -85,16 +85,17 @@ protected:
virtual void Cleanup() {};
nsresult ResetInputBuffers();
nsresult ResetOutputBuffers();
nsresult GetInputBuffer(JNIEnv* env, int index, jni::Object::LocalRef* buffer);
bool WaitForInput();
+ virtual bool WaitForKeys() { return false; }
already_AddRefed<MediaRawData> PeekNextSample();
nsresult QueueSample(const MediaRawData* aSample);
nsresult QueueEOS();
void HandleEOS(int32_t aOutputStatus);
Maybe<media::TimeUnit> GetOutputDuration();
nsresult ProcessOutput(java::sdk::BufferInfo::Param aInfo,
java::sdk::MediaFormat::Param aFormat,
int32_t aStatus);
--- a/mobile/android/base/java/org/mozilla/gecko/media/GeckoMediaDrm.java
+++ b/mobile/android/base/java/org/mozilla/gecko/media/GeckoMediaDrm.java
@@ -1,15 +1,16 @@
/* 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.media.MediaCrypto;
+import java.util.concurrent.locks.ReentrantLock;
public interface GeckoMediaDrm {
public interface Callbacks {
void onSessionCreated(int createSessionToken,
int promiseId,
byte[] sessionId,
byte[] request);
void onSessionUpdated(int promiseId, byte[] sessionId);
@@ -27,9 +28,15 @@ public interface GeckoMediaDrm {
void createSession(int createSessionToken,
int promiseId,
String initDataType,
byte[] initData);
void updateSession(int promiseId, String sessionId, byte[] response);
void closeSession(int promiseId, String sessionId);
void release();
MediaCrypto getMediaCrypto();
+
+ // The following interfaces are used for Android L or older.
+ void notifyKeyNeeded();
+ boolean waitForKey();
+ void doWait();
+ ReentrantLock getKeyNeededLock();
}
--- a/mobile/android/base/java/org/mozilla/gecko/media/GeckoMediaDrmBridgeV21.java
+++ b/mobile/android/base/java/org/mozilla/gecko/media/GeckoMediaDrmBridgeV21.java
@@ -11,16 +11,18 @@ import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.HashSet;
import java.util.UUID;
import java.util.ArrayDeque;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.concurrent.locks.Condition;
import android.annotation.SuppressLint;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.HandlerThread;
import android.media.DeniedByServerException;
import android.media.MediaCrypto;
import android.media.MediaCryptoException;
@@ -45,21 +47,28 @@ public class GeckoMediaDrmBridgeV21 impl
private HandlerThread mHandlerThread;
private ByteBuffer mCryptoSessionId;
// mProvisioningPromiseId is great than 0 only during provisioning.
private int mProvisioningPromiseId;
private HashSet<ByteBuffer> mSessionIds;
private HashMap<ByteBuffer, String> mSessionMIMETypes;
private ArrayDeque<PendingCreateSessionData> mPendingCreateSessionDataQueue;
- private GeckoMediaDrm.Callbacks mCallbacks;
+
+ private boolean mKeyNeeded = false;
+ private final ReentrantLock mKeyNeededLock = new ReentrantLock();
+ private final Condition mKeyNeededCondition = mKeyNeededLock.newCondition();
private MediaCrypto mCrypto;
+
protected MediaDrm mDrm;
+ protected GeckoMediaDrm.Callbacks mCallbacks;
+ // These flags are actually supported on API 23+, so we are able to use
+ // LICENSE_REQUEST_INITIAL only as the returing message type to web.
public static final int LICENSE_REQUEST_INITIAL = 0; /*MediaKeyMessageType::License_request*/
public static final int LICENSE_REQUEST_RENEWAL = 1; /*MediaKeyMessageType::License_renewal*/
public static final int LICENSE_REQUEST_RELEASE = 2; /*MediaKeyMessageType::License_release*/
// Store session data while provisioning
private static class PendingCreateSessionData {
public final int mToken;
public final int mPromiseId;
@@ -77,20 +86,20 @@ public class GeckoMediaDrmBridgeV21 impl
public boolean isSecureDecoderComonentRequired(String mimeType) {
if (mCrypto != null) {
return mCrypto.requiresSecureDecoderComponent(mimeType);
}
return false;
}
- private static void assertTrue(boolean condition) {
- if (DEBUG && !condition) {
- throw new AssertionError("Expected condition to be true");
- }
+ protected static void assertTrue(boolean condition) {
+ if (DEBUG && !condition) {
+ throw new AssertionError("Expected condition to be true");
+ }
}
@SuppressLint("WrongConstant")
private void configureVendorSpecificProperty() {
assertTrue(mDrm != null);
// Support L3 for now
mDrm.setPropertyString("securityLevel", "L3");
// Refer to chromium, set multi-session mode for Widevine.
@@ -113,16 +122,17 @@ public class GeckoMediaDrmBridgeV21 impl
if (DEBUG) Log.d(LOGTAG, "mSchemeUUID : " + mSchemeUUID.toString());
// The caller of GeckoMediaDrmBridgeV21 ctor should handle exceptions
// threw by the following steps.
mDrm = new MediaDrm(mSchemeUUID);
configureVendorSpecificProperty();
mDrm.setOnEventListener(new MediaDrmListener());
+ ensureMediaCryptoCreated();
}
@Override
public void setCallbacks(GeckoMediaDrm.Callbacks callbacks) {
assertTrue(callbacks != null);
mCallbacks = callbacks;
}
@@ -198,32 +208,35 @@ public class GeckoMediaDrmBridgeV21 impl
}
ByteBuffer session = ByteBuffer.wrap(sessionId.getBytes());
if (!sessionExists(session)) {
onRejectPromise(promiseId, "Invalid session during updateSession.");
return;
}
+ mKeyNeededLock.lock();
try {
final byte [] keySetId = mDrm.provideKeyResponse(session.array(), response);
if (DEBUG) {
HashMap<String, String> infoMap = mDrm.queryKeyStatus(session.array());
for (String strKey : infoMap.keySet()) {
String strValue = infoMap.get(strKey);
Log.d(LOGTAG, "InfoMap : key(" + strKey + ")/value(" + strValue + ")");
}
}
- HandleKeyStatusChangeByDummyKey(sessionId);
+ notifyKeyAdded();
onSessionUpdated(promiseId, session.array());
return;
} catch (final NotProvisionedException | DeniedByServerException | IllegalStateException e) {
if (DEBUG) Log.d(LOGTAG, "Failed to provide key response:", e);
onSessionError(session.array(), "Got exception during updateSession.");
onRejectPromise(promiseId, "Got exception during updateSession.");
+ } finally {
+ mKeyNeededLock.unlock();
}
release();
return;
}
@Override
public void closeSession(int promiseId, String sessionId) {
if (DEBUG) Log.d(LOGTAG, "closeSession()");
@@ -276,23 +289,69 @@ public class GeckoMediaDrmBridgeV21 impl
}
@Override
public MediaCrypto getMediaCrypto() {
if (DEBUG) Log.d(LOGTAG, "getMediaCrypto()");
return mCrypto;
}
- protected void HandleKeyStatusChangeByDummyKey(String sessionId)
- {
- SessionKeyInfo[] keyInfos = new SessionKeyInfo[1];
- keyInfos[0] = new SessionKeyInfo(DUMMY_KEY_ID,
- MediaDrm.KeyStatus.STATUS_USABLE);
- onSessionBatchedKeyChanged(sessionId.getBytes(), keyInfos);
- if (DEBUG) Log.d(LOGTAG, "Key successfully added for session " + sessionId);
+ @Override
+ public void notifyKeyNeeded() {
+ if (DEBUG) Log.d(LOGTAG, "notifyKeyNeeded()");
+ mKeyNeededLock.lock();
+ try {
+ mKeyNeeded = true;
+ } finally {
+ mKeyNeededLock.unlock();
+ }
+ }
+
+ @Override
+ public void doWait() {
+ if (DEBUG) Log.d(LOGTAG, "doWait()");
+ mKeyNeededLock.lock();
+ try {
+ if (mKeyNeeded) {
+ if (DEBUG) Log.d(LOGTAG, "doWait() => going to await");
+ mKeyNeededCondition.await();
+ }
+ } catch (Exception e) {
+ Log.e(LOGTAG, "Exception while calling doWait(),", e);
+ } finally {
+ mKeyNeededLock.unlock();
+ }
+ }
+
+ @Override
+ public synchronized boolean waitForKey() {
+ if (DEBUG) Log.d(LOGTAG, "waitForKey():" + mKeyNeeded);
+ return mKeyNeeded;
+ }
+
+ @Override
+ public ReentrantLock getKeyNeededLock() {
+ if (DEBUG) Log.d(LOGTAG, "getKeyNeededLock()");
+ return mKeyNeededLock;
+ }
+
+ protected void notifyKeyAdded() {
+ if (DEBUG) Log.d(LOGTAG, "notifyKeyAdded()");
+ mKeyNeededLock.lock();
+ try {
+ if (mKeyNeeded) {
+ if (DEBUG) Log.d(LOGTAG, "notifyKeyAdded() => keyAdded, going to signalAll");
+ mKeyNeeded = false;
+ mKeyNeededCondition.signalAll();
+ }
+ } catch (Exception e) {
+ Log.e(LOGTAG, "Exception while calling notifyKeyAdded()", e);
+ } finally {
+ mKeyNeededLock.unlock();
+ }
}
protected void onSessionCreated(int createSessionToken,
int promiseId,
byte[] sessionId,
byte[] request) {
assertTrue(mCallbacks != null);
mCallbacks.onSessionCreated(createSessionToken, promiseId, sessionId, request);
--- a/mobile/android/base/java/org/mozilla/gecko/media/GeckoMediaDrmBridgeV23.java
+++ b/mobile/android/base/java/org/mozilla/gecko/media/GeckoMediaDrmBridgeV23.java
@@ -10,16 +10,17 @@ import android.media.NotProvisionedExcep
import static android.os.Build.VERSION_CODES.M;
import android.media.MediaDrm;
import android.util.Log;
import java.lang.IllegalStateException;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.List;
+import java.util.concurrent.locks.ReentrantLock;
@TargetApi(M)
public class GeckoMediaDrmBridgeV23 extends GeckoMediaDrmBridgeV21 {
private static final boolean DEBUG = false;
GeckoMediaDrmBridgeV23(String keySystem) throws Exception {
super(keySystem);
if (DEBUG) Log.d(LOGTAG, "GeckoMediaDrmBridgeV23 ctor");
@@ -43,14 +44,38 @@ public class GeckoMediaDrmBridgeV23 exte
keyStatus.getStatusCode());
}
onSessionBatchedKeyChanged(sessionId, keyInfos);
if (DEBUG) Log.d(LOGTAG, "Key successfully added for session " + new String(sessionId));
}
}
@Override
- protected void HandleKeyStatusChangeByDummyKey(String sessionId)
- {
- // MediaDrm.KeyStatus information listener is supported on M+, there is no need to use
- // dummy key id to report key status anymore.
+ protected void notifyKeyAdded() {
+ // Do nothing on Android M+.
+ }
+
+ @Override
+ public void notifyKeyNeeded() {
+ // Should not be used on Android M+.
+ assertTrue(false);
+ }
+
+ @Override
+ public boolean waitForKey() {
+ // Should not be used on Android M+.
+ assertTrue(false);
+ return false;
+ }
+
+ @Override
+ public void doWait() {
+ // Should not be used on Android M+.
+ assertTrue(false);
+ }
+
+ @Override
+ public ReentrantLock getKeyNeededLock() {
+ // Should not be used on Android M+.
+ assertTrue(false);
+ return null;
}
}
--- a/mobile/android/base/java/org/mozilla/gecko/media/LocalMediaDrmBridge.java
+++ b/mobile/android/base/java/org/mozilla/gecko/media/LocalMediaDrmBridge.java
@@ -1,15 +1,17 @@
/* 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 org.mozilla.gecko.AppConstants;
+import java.util.concurrent.locks.ReentrantLock;
+
import android.media.MediaCrypto;
import android.util.Log;
final class LocalMediaDrmBridge implements GeckoMediaDrm {
private static final String LOGTAG = "GeckoLocalMediaDrmBridge";
private static final boolean DEBUG = false;
private GeckoMediaDrm mBridge = null;
private CallbacksForwarder mCallbacksFwd;
@@ -77,17 +79,17 @@ final class LocalMediaDrmBridge implemen
mProxyCallbacks.onRejectPromise(promiseId, message);
}
} // CallbacksForwarder
private static void assertTrue(boolean condition) {
if (DEBUG && !condition) {
throw new AssertionError("Expected condition to be true");
}
- }
+ }
LocalMediaDrmBridge(String keySystem) throws Exception {
if (AppConstants.Versions.preLollipop) {
Log.e(LOGTAG, "Pre-Lollipop should never enter here!!");
mBridge = null;
} else if (AppConstants.Versions.preMarshmallow) {
mBridge = new GeckoMediaDrmBridgeV21(keySystem);
} else {
@@ -154,9 +156,33 @@ final class LocalMediaDrmBridge implemen
}
}
@Override
public synchronized MediaCrypto getMediaCrypto() {
if (DEBUG) Log.d(LOGTAG, "getMediaCrypto()");
return mBridge != null ? mBridge.getMediaCrypto() : null;
}
+
+ @Override
+ public void notifyKeyNeeded() {
+ assertTrue(mBridge != null);
+ mBridge.notifyKeyNeeded();
+ }
+
+ @Override
+ public boolean waitForKey() {
+ assertTrue(mBridge != null);
+ return mBridge.waitForKey();
+ }
+
+ @Override
+ public void doWait() {
+ assertTrue(mBridge != null);
+ mBridge.doWait();
+ }
+
+ @Override
+ public ReentrantLock getKeyNeededLock() {
+ assertTrue(mBridge != null);
+ return mBridge.getKeyNeededLock();
+ }
}
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/java/org/mozilla/gecko/media/MediaCodecUtil.java
@@ -0,0 +1,77 @@
+/* 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 java.util.concurrent.locks.ReentrantLock;
+import android.media.MediaCodec;
+import android.util.Log;
+
+import org.mozilla.gecko.annotation.WrapForJNI;
+
+public final class MediaCodecUtil {
+ private static final String LOGTAG = "GeckoMediaCodecUtil";
+ private static final boolean DEBUG = false;
+
+ @WrapForJNI
+ public static final int CODEC_STATUS_OK = 0;
+ @WrapForJNI
+ public static final int CODEC_STATUS_NO_KEY = 1;
+ @WrapForJNI
+ public static final int CODEC_STATUS_ERROR = 2;
+
+ @WrapForJNI
+ public static int queueSecureInputBufferV23(MediaCodec codec,
+ int index,
+ int offset,
+ MediaCodec.CryptoInfo cryptoInfo,
+ long presentationTimeUs,
+ int flags) {
+ try {
+ codec.queueSecureInputBuffer(index, offset, cryptoInfo, presentationTimeUs, flags);
+ } catch (MediaCodec.CryptoException e) {
+ if (e.getErrorCode() == MediaCodec.CryptoException.ERROR_NO_KEY) {
+ if (DEBUG) Log.d(LOGTAG, "Got CryptoException.ERROR_NO_KEY");
+ return CODEC_STATUS_NO_KEY;
+ }
+ return CODEC_STATUS_ERROR;
+ } catch (Exception e) {
+ return CODEC_STATUS_ERROR;
+ }
+ return CODEC_STATUS_OK;
+ }
+
+ @WrapForJNI
+ public static int queueSecureInputBufferV21(MediaCodec codec,
+ String drmStubId,
+ int index,
+ int offset,
+ MediaCodec.CryptoInfo cryptoInfo,
+ long presentationTimeUs,
+ int flags) {
+ ReentrantLock lock = MediaDrmProxy.getKeyNeededLock(drmStubId);
+ if (lock == null) {
+ return CODEC_STATUS_ERROR;
+ }
+ int status = CODEC_STATUS_OK;
+ lock.lock();
+ try {
+ codec.queueSecureInputBuffer(index, offset, cryptoInfo, presentationTimeUs, flags);
+ } catch (MediaCodec.CryptoException e) {
+ if (e.getErrorCode() == MediaCodec.CryptoException.ERROR_NO_KEY) {
+ if (DEBUG) Log.d(LOGTAG, "Got CryptoException.ERROR_NO_KEY");
+ MediaDrmProxy.notifyKeyNeeded(drmStubId);
+ status = CODEC_STATUS_NO_KEY;
+ } else {
+ status = CODEC_STATUS_ERROR;
+ }
+ } catch (Exception e) {
+ status = CODEC_STATUS_ERROR;
+ } finally {
+ lock.unlock();
+ }
+ return status;
+ }
+}
--- a/mobile/android/base/java/org/mozilla/gecko/media/MediaDrmProxy.java
+++ b/mobile/android/base/java/org/mozilla/gecko/media/MediaDrmProxy.java
@@ -1,17 +1,18 @@
/* 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 java.util.ArrayList;
+import java.util.HashMap;
import java.util.UUID;
+import java.util.concurrent.locks.ReentrantLock;
import org.mozilla.gecko.mozglue.JNIObject;
import org.mozilla.gecko.annotation.WrapForJNI;
import org.mozilla.gecko.AppConstants;
import android.media.MediaCodecInfo;
import android.media.MediaCodecList;
import android.media.MediaCrypto;
@@ -34,17 +35,21 @@ public final class MediaDrmProxy {
private static final String VORBIS = "audio/vorbis";
@WrapForJNI
private static final String VP8 = "video/x-vnd.on2.vp8";
@WrapForJNI
private static final String VP9 = "video/x-vnd.on2.vp9";
@WrapForJNI
private static final String OPUS = "audio/opus";
- public static final ArrayList<MediaDrmProxy> sProxyList = new ArrayList<MediaDrmProxy>();
+ @WrapForJNI
+ private static boolean isPreMarshmallow = AppConstants.Versions.preMarshmallow;
+
+ public static final HashMap<String, MediaDrmProxy> sProxyMap =
+ new HashMap<String, MediaDrmProxy>();
// A flag to avoid using the native object that has been destroyed.
private boolean mDestroyed;
private GeckoMediaDrm mImpl;
private String mDrmStubId;
private static boolean isSystemSupported() {
// Support versions >= LOLLIPOP
@@ -263,17 +268,17 @@ public final class MediaDrmProxy {
if (isRemote) {
IMediaDrmBridge remoteBridge =
RemoteManager.getInstance().createRemoteMediaDrmBridge(keySystem, mDrmStubId);
mImpl = new RemoteMediaDrmBridge(remoteBridge);
} else {
mImpl = new LocalMediaDrmBridge(keySystem);
}
mImpl.setCallbacks(new MediaDrmProxyCallbacks(this, nativeCallbacks));
- sProxyList.add(this);
+ sProxyMap.put(mDrmStubId, this);
} catch (Exception e) {
Log.e(LOGTAG, "Constructing MediaDrmProxy ... error", e);
}
}
@WrapForJNI
private void createSession(int createSessionToken,
int promiseId,
@@ -302,37 +307,111 @@ public final class MediaDrmProxy {
private String getStubId() {
return mDrmStubId;
}
// Get corresponding MediaCrypto object by a generated UUID for MediaCodec.
// Will be called on MediaFormatReader's TaskQueue.
@WrapForJNI
public static MediaCrypto getMediaCrypto(String stubId) {
- for (MediaDrmProxy proxy : sProxyList) {
- if (proxy.getStubId().equals(stubId)) {
- return proxy.getMediaCryptoFromBridge();
- }
+ MediaDrmProxy proxy = sProxyMap.get(stubId);
+ if (proxy == null) {
+ if (DEBUG) Log.d(LOGTAG, " NULL crytpo ");
+ return null;
}
- if (DEBUG) Log.d(LOGTAG, " NULL crytpo ");
- return null;
+ return proxy.getMediaCryptoFromBridge();
}
@WrapForJNI // Called when natvie object is destroyed.
private void destroy() {
if (DEBUG) Log.d(LOGTAG, "destroy!! Native object is destroyed.");
if (mDestroyed) {
return;
}
mDestroyed = true;
release();
}
private void release() {
if (DEBUG) Log.d(LOGTAG, "release");
- sProxyList.remove(this);
+ sProxyMap.remove(mDrmStubId);
mImpl.release();
}
private MediaCrypto getMediaCryptoFromBridge() {
return mImpl != null ? mImpl.getMediaCrypto() : null;
}
+
+ private static void assertTrue(boolean condition) {
+ if (DEBUG && !condition) {
+ throw new AssertionError("Expected condition to be true");
+ }
+ }
+
+ private void notifyKeyNeeded() {
+ assertTrue(mImpl != null);
+ if (DEBUG) Log.d(LOGTAG, "notifyKeyNeeded()");
+ mImpl.notifyKeyNeeded();
+ }
+
+ @WrapForJNI
+ public static void notifyKeyNeeded(String stubId) {
+ assertTrue(AppConstants.Versions.preMarshmallow);
+ MediaDrmProxy proxy = sProxyMap.get(stubId);
+ if (proxy == null) {
+ if (DEBUG) Log.d(LOGTAG, "notifyKeyNeeded() error, cannot find targe DRM stub :" + stubId);
+ }
+ proxy.notifyKeyNeeded();
+ }
+
+ private boolean waitForKey() {
+ assertTrue(mImpl != null);
+ if (DEBUG) Log.d(LOGTAG, "waitForKey()");
+ return mImpl.waitForKey();
+ }
+
+ @WrapForJNI
+ public static boolean waitForKey(String stubId) {
+ assertTrue(AppConstants.Versions.preMarshmallow);
+
+ MediaDrmProxy proxy = sProxyMap.get(stubId);
+ if (proxy == null) {
+ if (DEBUG) Log.d(LOGTAG, "waitForKey() error, cannot find targe DRM stub :" + stubId);
+ return false;
+ }
+ return proxy.waitForKey();
+ }
+
+ private void doWait() {
+ assertTrue(mImpl != null);
+ if (DEBUG) Log.d(LOGTAG, "doWait()");
+ mImpl.doWait();
+ }
+
+ @WrapForJNI
+ public static void doWait(String stubId) {
+ assertTrue(AppConstants.Versions.preMarshmallow);
+
+ MediaDrmProxy proxy = sProxyMap.get(stubId);
+ if (proxy == null) {
+ if (DEBUG) Log.d(LOGTAG, "doWait() error, cannot find targe DRM stub :" + stubId);
+ return;
+ }
+ proxy.doWait();
+ }
+
+ private ReentrantLock getKeyNeededLock() {
+ assertTrue(mImpl != null);
+ if (DEBUG) Log.d(LOGTAG, "getKeyNeededLock()");
+ return mImpl.getKeyNeededLock();
+ }
+
+ public static ReentrantLock getKeyNeededLock(String stubId) {
+ assertTrue(AppConstants.Versions.preMarshmallow);
+
+ MediaDrmProxy proxy = sProxyMap.get(stubId);
+ if (proxy == null) {
+ if (DEBUG) Log.d(LOGTAG, "getKeyNeededLock() error, cannot find targe DRM stub :" + stubId);
+ return null;
+ }
+ return proxy.getKeyNeededLock();
+ }
}
--- a/mobile/android/base/java/org/mozilla/gecko/media/RemoteMediaDrmBridge.java
+++ b/mobile/android/base/java/org/mozilla/gecko/media/RemoteMediaDrmBridge.java
@@ -1,14 +1,15 @@
/* 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 java.util.concurrent.locks.ReentrantLock;
import android.media.MediaCrypto;
import android.util.Log;
final class RemoteMediaDrmBridge implements GeckoMediaDrm {
private static final String LOGTAG = "GeckoRemoteMediaDrmBridge";
private static final boolean DEBUG = false;
private CallbacksForwarder mCallbacksFwd;
private IMediaDrmBridge mRemote;
@@ -144,9 +145,35 @@ final class RemoteMediaDrmBridge impleme
}
@Override
public synchronized MediaCrypto getMediaCrypto() {
if (DEBUG) Log.d(LOGTAG, "getMediaCrypto(), should not enter here!");
assertTrue(false);
return null;
}
+
+ @Override
+ public void notifyKeyNeeded() {
+ // Should not be used in this case.
+ assertTrue(false);
+ }
+
+ @Override
+ public boolean waitForKey() {
+ // Should not be used in this case.
+ assertTrue(false);
+ return false;
+ }
+
+ @Override
+ public void doWait() {
+ // Should not be used in this case.
+ assertTrue(false);
+ }
+
+ @Override
+ public ReentrantLock getKeyNeededLock() {
+ // Should not be used in this case.
+ assertTrue(false);
+ return null;
+ }
}
\ No newline at end of file
--- a/mobile/android/base/java/org/mozilla/gecko/media/RemoteMediaDrmBridgeStub.java
+++ b/mobile/android/base/java/org/mozilla/gecko/media/RemoteMediaDrmBridgeStub.java
@@ -1,16 +1,17 @@
/* 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 org.mozilla.gecko.AppConstants;
-import java.util.ArrayList;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.HashMap;
import android.media.MediaCrypto;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
final class RemoteMediaDrmBridgeStub extends IMediaDrmBridge.Stub implements IBinder.DeathRecipient {
private static final String LOGTAG = "GeckoRemoteMediaDrmBridgeStub";
@@ -23,37 +24,98 @@ final class RemoteMediaDrmBridgeStub ext
// mStubId is initialized during stub construction. It should be a unique
// string which is generated in MediaDrmProxy in Fennec App process and is
// used for Codec to obtain corresponding MediaCrypto as input to achieve
// decryption.
// The generated stubId will be delivered to Codec via a code path starting
// from MediaDrmProxy -> MediaDrmCDMProxy -> RemoteDataDecoder => IPC => Codec.
private String mStubId = "";
- public static ArrayList<RemoteMediaDrmBridgeStub> mBridgeStubs =
- new ArrayList<RemoteMediaDrmBridgeStub>();
+ public static HashMap<String, RemoteMediaDrmBridgeStub> sBridgeStubMap =
+ new HashMap<String, RemoteMediaDrmBridgeStub>();
private String getId() {
return mStubId;
}
private MediaCrypto getMediaCryptoFromBridge() {
return mBridge != null ? mBridge.getMediaCrypto() : null;
}
public static synchronized MediaCrypto getMediaCrypto(String stubId) {
if (DEBUG) Log.d(LOGTAG, "getMediaCrypto()");
+ RemoteMediaDrmBridgeStub bridgeStub = sBridgeStubMap.get(stubId);
+ if (bridgeStub == null) {
+ return null;
+ }
+ return bridgeStub.getMediaCryptoFromBridge();
+ }
- for (int i = 0; i < mBridgeStubs.size(); i++) {
- if (mBridgeStubs.get(i) != null &&
- mBridgeStubs.get(i).getId().equals(stubId)) {
- return mBridgeStubs.get(i).getMediaCryptoFromBridge();
- }
+ public static void notifyKeyNeeded(String stubId) {
+ assertTrue(AppConstants.Versions.preMarshmallow);
+ if (DEBUG) Log.d(LOGTAG, "notifyKeyNeeded()");
+ RemoteMediaDrmBridgeStub bridgeStub = sBridgeStubMap.get(stubId);
+ if (bridgeStub == null) {
+ if (DEBUG) Log.d(LOGTAG, "notifyKeyNeeded() error, find no stub !");
+ return;
+ }
+ bridgeStub.notifyKeyNeeded();
+ }
+
+ private void notifyKeyNeeded() {
+ assertTrue(mBridge != null);
+ mBridge.notifyKeyNeeded();
+ }
+
+ public static boolean waitForKey(String stubId) {
+ assertTrue(AppConstants.Versions.preMarshmallow);
+ if (DEBUG) Log.d(LOGTAG, "waitForKey()");
+ RemoteMediaDrmBridgeStub bridgeStub = sBridgeStubMap.get(stubId);
+ if (bridgeStub == null) {
+ if (DEBUG) Log.d(LOGTAG, "waitForKey() error, find no stub !");
+ return false;
}
- return null;
+ return bridgeStub.waitForKey();
+ }
+
+ private void doWait() {
+ assertTrue(mBridge != null);
+ mBridge.doWait();
+ }
+
+ public static void doWait(String stubId) {
+ assertTrue(AppConstants.Versions.preMarshmallow);
+ if (DEBUG) Log.d(LOGTAG, "wait()");
+ RemoteMediaDrmBridgeStub bridgeStub = sBridgeStubMap.get(stubId);
+ if (bridgeStub == null) {
+ if (DEBUG) Log.d(LOGTAG, "wait() error, find no stub !");
+ return;
+ }
+ bridgeStub.doWait();
+ }
+
+ private boolean waitForKey() {
+ assertTrue(mBridge != null);
+ return mBridge.waitForKey();
+ }
+
+ private ReentrantLock getKeyNeededLock() {
+ assertTrue(mBridge != null);
+ return mBridge.getKeyNeededLock();
+ }
+
+ public static ReentrantLock getKeyNeededLock(String stubId) {
+ assertTrue(AppConstants.Versions.preMarshmallow);
+ if (DEBUG) Log.d(LOGTAG, "getKeyNeededLock()");
+ RemoteMediaDrmBridgeStub bridgeStub = sBridgeStubMap.get(stubId);
+ if (bridgeStub == null) {
+ if (DEBUG) Log.d(LOGTAG, "getKeyNeededLock() error, find no stub !");
+ return null;
+ }
+ return bridgeStub.getKeyNeededLock();
}
// Callback to RemoteMediaDrmBridge.
private final class Callbacks implements GeckoMediaDrm.Callbacks {
private IMediaDrmBridgeCallbacks mRemoteCallbacks;
public Callbacks(IMediaDrmBridgeCallbacks remote) {
mRemoteCallbacks = remote;
@@ -134,17 +196,17 @@ final class RemoteMediaDrmBridgeStub ext
try {
mRemoteCallbacks.onRejectPromise(promiseId, message);
} catch (RemoteException e) {
Log.e(LOGTAG, "Exception ! Dead recipient !!", e);
}
}
}
- /* package-private */ void assertTrue(boolean condition) {
+ /* package-private */ static void assertTrue(boolean condition) {
if (DEBUG && !condition) {
throw new AssertionError("Expected condition to be true");
}
}
RemoteMediaDrmBridgeStub(String keySystem, String stubId) throws RemoteException {
if (AppConstants.Versions.preLollipop) {
Log.e(LOGTAG, "Pre-Lollipop should never enter here!!");
@@ -152,17 +214,17 @@ final class RemoteMediaDrmBridgeStub ext
}
try {
if (AppConstants.Versions.preMarshmallow) {
mBridge = new GeckoMediaDrmBridgeV21(keySystem);
} else {
mBridge = new GeckoMediaDrmBridgeV23(keySystem);
}
mStubId = stubId;
- mBridgeStubs.add(this);
+ sBridgeStubMap.put(mStubId, this);
} catch (Exception e) {
throw new RemoteException("RemoteMediaDrmBridgeStub cannot create bridge implementation.");
}
}
@Override
public synchronized void setCallbacks(IMediaDrmBridgeCallbacks callbacks) throws RemoteException {
if (DEBUG) Log.d(LOGTAG, "setCallbacks()");
@@ -229,17 +291,17 @@ final class RemoteMediaDrmBridgeStub ext
} catch (Exception e) {
Log.e(LOGTAG, "Exception ! Dead recipient !!", e);
}
}
@Override
public synchronized void release() {
if (DEBUG) Log.d(LOGTAG, "release()");
- mBridgeStubs.remove(this);
+ sBridgeStubMap.remove(mStubId);
if (mBridge != null) {
mBridge.release();
mBridge = null;
}
mCallbacks.asBinder().unlinkToDeath(this, 0);
mCallbacks = null;
mStubId = "";
}
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -562,16 +562,17 @@ gbjar.sources += ['java/org/mozilla/geck
'media/Codec.java',
'media/CodecProxy.java',
'media/FormatParam.java',
'media/GeckoMediaDrm.java',
'media/GeckoMediaDrmBridgeV21.java',
'media/GeckoMediaDrmBridgeV23.java',
'media/JellyBeanAsyncCodec.java',
'media/LocalMediaDrmBridge.java',
+ 'media/MediaCodecUtil.java',
'media/MediaControlService.java',
'media/MediaDrmProxy.java',
'media/MediaManager.java',
'media/RemoteManager.java',
'media/RemoteMediaDrmBridge.java',
'media/RemoteMediaDrmBridgeStub.java',
'media/Sample.java',
'media/SamplePool.java',
--- a/widget/android/fennec/FennecJNIWrappers.cpp
+++ b/widget/android/fennec/FennecJNIWrappers.cpp
@@ -242,16 +242,35 @@ constexpr char CodecProxy::NativeCallbac
constexpr char CodecProxy::NativeCallbacks::OnInputExhausted_t::signature[];
constexpr char CodecProxy::NativeCallbacks::OnOutput_t::name[];
constexpr char CodecProxy::NativeCallbacks::OnOutput_t::signature[];
constexpr char CodecProxy::NativeCallbacks::OnOutputFormatChanged_t::name[];
constexpr char CodecProxy::NativeCallbacks::OnOutputFormatChanged_t::signature[];
+const char MediaCodecUtil::name[] =
+ "org/mozilla/gecko/media/MediaCodecUtil";
+
+constexpr char MediaCodecUtil::QueueSecureInputBufferV21_t::name[];
+constexpr char MediaCodecUtil::QueueSecureInputBufferV21_t::signature[];
+
+auto MediaCodecUtil::QueueSecureInputBufferV21(mozilla::jni::Object::Param a0, mozilla::jni::String::Param a1, int32_t a2, int32_t a3, mozilla::jni::Object::Param a4, int64_t a5, int32_t a6) -> int32_t
+{
+ return mozilla::jni::Method<QueueSecureInputBufferV21_t>::Call(MediaCodecUtil::Context(), nullptr, a0, a1, a2, a3, a4, a5, a6);
+}
+
+constexpr char MediaCodecUtil::QueueSecureInputBufferV23_t::name[];
+constexpr char MediaCodecUtil::QueueSecureInputBufferV23_t::signature[];
+
+auto MediaCodecUtil::QueueSecureInputBufferV23(mozilla::jni::Object::Param a0, int32_t a1, int32_t a2, mozilla::jni::Object::Param a3, int64_t a4, int32_t a5) -> int32_t
+{
+ return mozilla::jni::Method<QueueSecureInputBufferV23_t>::Call(MediaCodecUtil::Context(), nullptr, a0, a1, a2, a3, a4, a5);
+}
+
const char MediaDrmProxy::name[] =
"org/mozilla/gecko/media/MediaDrmProxy";
constexpr char MediaDrmProxy::CanDecode_t::name[];
constexpr char MediaDrmProxy::CanDecode_t::signature[];
auto MediaDrmProxy::CanDecode(mozilla::jni::String::Param a0) -> bool
{
@@ -293,16 +312,24 @@ auto MediaDrmProxy::CreateSession(int32_
constexpr char MediaDrmProxy::Destroy_t::name[];
constexpr char MediaDrmProxy::Destroy_t::signature[];
auto MediaDrmProxy::Destroy() const -> void
{
return mozilla::jni::Method<Destroy_t>::Call(MediaDrmProxy::mCtx, nullptr);
}
+constexpr char MediaDrmProxy::DoWait_t::name[];
+constexpr char MediaDrmProxy::DoWait_t::signature[];
+
+auto MediaDrmProxy::DoWait(mozilla::jni::String::Param a0) -> void
+{
+ return mozilla::jni::Method<DoWait_t>::Call(MediaDrmProxy::Context(), nullptr, a0);
+}
+
constexpr char MediaDrmProxy::GetMediaCrypto_t::name[];
constexpr char MediaDrmProxy::GetMediaCrypto_t::signature[];
auto MediaDrmProxy::GetMediaCrypto(mozilla::jni::String::Param a0) -> mozilla::jni::Object::LocalRef
{
return mozilla::jni::Method<GetMediaCrypto_t>::Call(MediaDrmProxy::Context(), nullptr, a0);
}
@@ -317,36 +344,65 @@ auto MediaDrmProxy::GetStubId() const ->
constexpr char MediaDrmProxy::IsSchemeSupported_t::name[];
constexpr char MediaDrmProxy::IsSchemeSupported_t::signature[];
auto MediaDrmProxy::IsSchemeSupported(mozilla::jni::String::Param a0) -> bool
{
return mozilla::jni::Method<IsSchemeSupported_t>::Call(MediaDrmProxy::Context(), nullptr, a0);
}
+constexpr char MediaDrmProxy::NotifyKeyNeeded_t::name[];
+constexpr char MediaDrmProxy::NotifyKeyNeeded_t::signature[];
+
+auto MediaDrmProxy::NotifyKeyNeeded(mozilla::jni::String::Param a0) -> void
+{
+ return mozilla::jni::Method<NotifyKeyNeeded_t>::Call(MediaDrmProxy::Context(), nullptr, a0);
+}
+
constexpr char MediaDrmProxy::UpdateSession_t::name[];
constexpr char MediaDrmProxy::UpdateSession_t::signature[];
auto MediaDrmProxy::UpdateSession(int32_t a0, mozilla::jni::String::Param a1, mozilla::jni::ByteArray::Param a2) const -> void
{
return mozilla::jni::Method<UpdateSession_t>::Call(MediaDrmProxy::mCtx, nullptr, a0, a1, a2);
}
+constexpr char MediaDrmProxy::WaitForKey_t::name[];
+constexpr char MediaDrmProxy::WaitForKey_t::signature[];
+
+auto MediaDrmProxy::WaitForKey(mozilla::jni::String::Param a0) -> bool
+{
+ return mozilla::jni::Method<WaitForKey_t>::Call(MediaDrmProxy::Context(), nullptr, a0);
+}
+
const char16_t MediaDrmProxy::AAC[] = u"audio/mp4a-latm";
const char16_t MediaDrmProxy::AVC[] = u"video/avc";
const char16_t MediaDrmProxy::OPUS[] = u"audio/opus";
const char16_t MediaDrmProxy::VORBIS[] = u"audio/vorbis";
const char16_t MediaDrmProxy::VP8[] = u"video/x-vnd.on2.vp8";
const char16_t MediaDrmProxy::VP9[] = u"video/x-vnd.on2.vp9";
+constexpr char MediaDrmProxy::IsPreMarshmallow_t::name[];
+constexpr char MediaDrmProxy::IsPreMarshmallow_t::signature[];
+
+auto MediaDrmProxy::IsPreMarshmallow() -> bool
+{
+ return mozilla::jni::Field<IsPreMarshmallow_t>::Get(MediaDrmProxy::Context(), nullptr);
+}
+
+auto MediaDrmProxy::IsPreMarshmallow(bool a0) -> void
+{
+ return mozilla::jni::Field<IsPreMarshmallow_t>::Set(MediaDrmProxy::Context(), nullptr, a0);
+}
+
const char MediaDrmProxy::NativeMediaDrmProxyCallbacks::name[] =
"org/mozilla/gecko/media/MediaDrmProxy$NativeMediaDrmProxyCallbacks";
constexpr char MediaDrmProxy::NativeMediaDrmProxyCallbacks::New_t::name[];
constexpr char MediaDrmProxy::NativeMediaDrmProxyCallbacks::New_t::signature[];
auto MediaDrmProxy::NativeMediaDrmProxyCallbacks::New() -> NativeMediaDrmProxyCallbacks::LocalRef
{
--- a/widget/android/fennec/FennecJNIWrappers.h
+++ b/widget/android/fennec/FennecJNIWrappers.h
@@ -885,16 +885,85 @@ public:
};
static const mozilla::jni::CallingThread callingThread =
mozilla::jni::CallingThread::ANY;
template<class Impl> class Natives;
};
+class MediaCodecUtil : public mozilla::jni::ObjectBase<MediaCodecUtil>
+{
+public:
+ static const char name[];
+
+ explicit MediaCodecUtil(const Context& ctx) : ObjectBase<MediaCodecUtil>(ctx) {}
+
+ struct QueueSecureInputBufferV21_t {
+ typedef MediaCodecUtil Owner;
+ typedef int32_t ReturnType;
+ typedef int32_t SetterType;
+ typedef mozilla::jni::Args<
+ mozilla::jni::Object::Param,
+ mozilla::jni::String::Param,
+ int32_t,
+ int32_t,
+ mozilla::jni::Object::Param,
+ int64_t,
+ int32_t> Args;
+ static constexpr char name[] = "queueSecureInputBufferV21";
+ static constexpr char signature[] =
+ "(Landroid/media/MediaCodec;Ljava/lang/String;IILandroid/media/MediaCodec$CryptoInfo;JI)I";
+ static const bool isStatic = true;
+ static const mozilla::jni::ExceptionMode exceptionMode =
+ mozilla::jni::ExceptionMode::ABORT;
+ static const mozilla::jni::CallingThread callingThread =
+ mozilla::jni::CallingThread::ANY;
+ static const mozilla::jni::DispatchTarget dispatchTarget =
+ mozilla::jni::DispatchTarget::CURRENT;
+ };
+
+ static auto QueueSecureInputBufferV21(mozilla::jni::Object::Param, mozilla::jni::String::Param, int32_t, int32_t, mozilla::jni::Object::Param, int64_t, int32_t) -> int32_t;
+
+ struct QueueSecureInputBufferV23_t {
+ typedef MediaCodecUtil Owner;
+ typedef int32_t ReturnType;
+ typedef int32_t SetterType;
+ typedef mozilla::jni::Args<
+ mozilla::jni::Object::Param,
+ int32_t,
+ int32_t,
+ mozilla::jni::Object::Param,
+ int64_t,
+ int32_t> Args;
+ static constexpr char name[] = "queueSecureInputBufferV23";
+ static constexpr char signature[] =
+ "(Landroid/media/MediaCodec;IILandroid/media/MediaCodec$CryptoInfo;JI)I";
+ static const bool isStatic = true;
+ static const mozilla::jni::ExceptionMode exceptionMode =
+ mozilla::jni::ExceptionMode::ABORT;
+ static const mozilla::jni::CallingThread callingThread =
+ mozilla::jni::CallingThread::ANY;
+ static const mozilla::jni::DispatchTarget dispatchTarget =
+ mozilla::jni::DispatchTarget::CURRENT;
+ };
+
+ static auto QueueSecureInputBufferV23(mozilla::jni::Object::Param, int32_t, int32_t, mozilla::jni::Object::Param, int64_t, int32_t) -> int32_t;
+
+ static const int32_t CODEC_STATUS_ERROR = 2;
+
+ static const int32_t CODEC_STATUS_NO_KEY = 1;
+
+ static const int32_t CODEC_STATUS_OK = 0;
+
+ static const mozilla::jni::CallingThread callingThread =
+ mozilla::jni::CallingThread::ANY;
+
+};
+
class MediaDrmProxy : public mozilla::jni::ObjectBase<MediaDrmProxy>
{
public:
static const char name[];
explicit MediaDrmProxy(const Context& ctx) : ObjectBase<MediaDrmProxy>(ctx) {}
class NativeMediaDrmProxyCallbacks;
@@ -1020,16 +1089,36 @@ public:
static const mozilla::jni::CallingThread callingThread =
mozilla::jni::CallingThread::ANY;
static const mozilla::jni::DispatchTarget dispatchTarget =
mozilla::jni::DispatchTarget::CURRENT;
};
auto Destroy() const -> void;
+ struct DoWait_t {
+ typedef MediaDrmProxy Owner;
+ typedef void ReturnType;
+ typedef void SetterType;
+ typedef mozilla::jni::Args<
+ mozilla::jni::String::Param> Args;
+ static constexpr char name[] = "doWait";
+ static constexpr char signature[] =
+ "(Ljava/lang/String;)V";
+ static const bool isStatic = true;
+ static const mozilla::jni::ExceptionMode exceptionMode =
+ mozilla::jni::ExceptionMode::ABORT;
+ static const mozilla::jni::CallingThread callingThread =
+ mozilla::jni::CallingThread::ANY;
+ static const mozilla::jni::DispatchTarget dispatchTarget =
+ mozilla::jni::DispatchTarget::CURRENT;
+ };
+
+ static auto DoWait(mozilla::jni::String::Param) -> void;
+
struct GetMediaCrypto_t {
typedef MediaDrmProxy Owner;
typedef mozilla::jni::Object::LocalRef ReturnType;
typedef mozilla::jni::Object::Param SetterType;
typedef mozilla::jni::Args<
mozilla::jni::String::Param> Args;
static constexpr char name[] = "getMediaCrypto";
static constexpr char signature[] =
@@ -1079,16 +1168,36 @@ public:
static const mozilla::jni::CallingThread callingThread =
mozilla::jni::CallingThread::ANY;
static const mozilla::jni::DispatchTarget dispatchTarget =
mozilla::jni::DispatchTarget::CURRENT;
};
static auto IsSchemeSupported(mozilla::jni::String::Param) -> bool;
+ struct NotifyKeyNeeded_t {
+ typedef MediaDrmProxy Owner;
+ typedef void ReturnType;
+ typedef void SetterType;
+ typedef mozilla::jni::Args<
+ mozilla::jni::String::Param> Args;
+ static constexpr char name[] = "notifyKeyNeeded";
+ static constexpr char signature[] =
+ "(Ljava/lang/String;)V";
+ static const bool isStatic = true;
+ static const mozilla::jni::ExceptionMode exceptionMode =
+ mozilla::jni::ExceptionMode::ABORT;
+ static const mozilla::jni::CallingThread callingThread =
+ mozilla::jni::CallingThread::ANY;
+ static const mozilla::jni::DispatchTarget dispatchTarget =
+ mozilla::jni::DispatchTarget::CURRENT;
+ };
+
+ static auto NotifyKeyNeeded(mozilla::jni::String::Param) -> void;
+
struct UpdateSession_t {
typedef MediaDrmProxy Owner;
typedef void ReturnType;
typedef void SetterType;
typedef mozilla::jni::Args<
int32_t,
mozilla::jni::String::Param,
mozilla::jni::ByteArray::Param> Args;
@@ -1101,28 +1210,69 @@ public:
static const mozilla::jni::CallingThread callingThread =
mozilla::jni::CallingThread::ANY;
static const mozilla::jni::DispatchTarget dispatchTarget =
mozilla::jni::DispatchTarget::CURRENT;
};
auto UpdateSession(int32_t, mozilla::jni::String::Param, mozilla::jni::ByteArray::Param) const -> void;
+ struct WaitForKey_t {
+ typedef MediaDrmProxy Owner;
+ typedef bool ReturnType;
+ typedef bool SetterType;
+ typedef mozilla::jni::Args<
+ mozilla::jni::String::Param> Args;
+ static constexpr char name[] = "waitForKey";
+ static constexpr char signature[] =
+ "(Ljava/lang/String;)Z";
+ static const bool isStatic = true;
+ static const mozilla::jni::ExceptionMode exceptionMode =
+ mozilla::jni::ExceptionMode::ABORT;
+ static const mozilla::jni::CallingThread callingThread =
+ mozilla::jni::CallingThread::ANY;
+ static const mozilla::jni::DispatchTarget dispatchTarget =
+ mozilla::jni::DispatchTarget::CURRENT;
+ };
+
+ static auto WaitForKey(mozilla::jni::String::Param) -> bool;
+
static const char16_t AAC[];
static const char16_t AVC[];
static const char16_t OPUS[];
static const char16_t VORBIS[];
static const char16_t VP8[];
static const char16_t VP9[];
+ struct IsPreMarshmallow_t {
+ typedef MediaDrmProxy Owner;
+ typedef bool ReturnType;
+ typedef bool SetterType;
+ typedef mozilla::jni::Args<> Args;
+ static constexpr char name[] = "isPreMarshmallow";
+ static constexpr char signature[] =
+ "Z";
+ static const bool isStatic = true;
+ static const mozilla::jni::ExceptionMode exceptionMode =
+ mozilla::jni::ExceptionMode::ABORT;
+ static const mozilla::jni::CallingThread callingThread =
+ mozilla::jni::CallingThread::ANY;
+ static const mozilla::jni::DispatchTarget dispatchTarget =
+ mozilla::jni::DispatchTarget::CURRENT;
+ };
+
+ static auto IsPreMarshmallow() -> bool;
+
+ static auto IsPreMarshmallow(bool) -> void;
+
static const mozilla::jni::CallingThread callingThread =
mozilla::jni::CallingThread::ANY;
};
class MediaDrmProxy::NativeMediaDrmProxyCallbacks : public mozilla::jni::ObjectBase<NativeMediaDrmProxyCallbacks>
{
public: