Bug 1317628-[P2] Handling key-needed/key-added/notifying for encrypted media playback with Fennec out-of-process-decode mode on Android L.
MozReview-Commit-ID: EXOng1Blb5M
--- a/mobile/android/base/java/org/mozilla/gecko/media/AsyncCodec.java
+++ b/mobile/android/base/java/org/mozilla/gecko/media/AsyncCodec.java
@@ -30,9 +30,12 @@ public interface AsyncCodec {
public abstract void stop();
public abstract void flush();
public abstract void release();
public abstract ByteBuffer getInputBuffer(int index);
public abstract ByteBuffer getOutputBuffer(int index);
public abstract void queueInputBuffer(int index, int offset, int size, long presentationTimeUs, int flags);
public abstract void queueSecureInputBuffer(int index, int offset, CryptoInfo info, long presentationTimeUs, int flags);
public abstract void releaseOutputBuffer(int index, boolean render);
+
+ public abstract void setDrmStubId(String drmStubId);
+ public abstract void notifyKeyAdded();
}
--- a/mobile/android/base/java/org/mozilla/gecko/media/Codec.java
+++ b/mobile/android/base/java/org/mozilla/gecko/media/Codec.java
@@ -1,14 +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 org.mozilla.gecko.util.MediaCodecUtil;
+
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaCodecList;
import android.media.MediaCrypto;
import android.media.MediaFormat;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.TransactionTooLargeException;
@@ -25,16 +28,29 @@ import java.util.concurrent.ConcurrentLi
/* package */ final class Codec extends ICodec.Stub implements IBinder.DeathRecipient {
private static final String LOGTAG = "GeckoRemoteCodec";
private static final boolean DEBUG = false;
public enum Error {
DECODE, FATAL
};
+ public final class CodecKeyWaiter implements MediaCodecUtil.KeyWaiter{
+ private AsyncCodec mCodecImpl;
+
+ CodecKeyWaiter(AsyncCodec codec) {
+ mCodecImpl = codec;
+ }
+
+ @Override
+ public void onKeyAdded() {
+ mCodecImpl.notifyKeyAdded();
+ }
+ }
+
private final class Callbacks implements AsyncCodec.Callbacks {
private ICodecCallbacks mRemote;
private boolean mHasInputCapacitySet;
private boolean mHasOutputCapacitySet;
public Callbacks(ICodecCallbacks remote) {
mRemote = remote;
}
@@ -158,16 +174,20 @@ import java.util.concurrent.ConcurrentLi
return false;
}
feedSampleToBuffer();
return true;
}
private void feedSampleToBuffer() {
while (!mAvailableInputBuffers.isEmpty() && !mInputSamples.isEmpty()) {
+ if (AppConstants.Versions.preMarshmallow &&
+ RemoteMediaDrmBridgeStub.waitForKey(mDrmStubId)) {
+ return;
+ }
int index = mAvailableInputBuffers.poll();
int len = 0;
Sample sample = mInputSamples.poll();
long pts = sample.info.presentationTimeUs;
int flags = sample.info.flags;
MediaCodec.CryptoInfo cryptoInfo = sample.cryptoInfo;
if (!sample.isEOS() && sample.buffer != null) {
len = sample.info.size;
@@ -200,16 +220,17 @@ import java.util.concurrent.ConcurrentLi
private volatile ICodecCallbacks mCallbacks;
private AsyncCodec mCodec;
private InputProcessor mInputProcessor;
private volatile boolean mFlushing = false;
private SamplePool mSamplePool;
private Queue<Sample> mSentOutputs = new ConcurrentLinkedQueue<>();
// Value will be updated after configure called.
private volatile boolean mIsAdaptivePlaybackSupported = false;
+ private String mDrmStubId;
public synchronized void setCallbacks(ICodecCallbacks callbacks) throws RemoteException {
mCallbacks = callbacks;
callbacks.asBinder().linkToDeath(this, 0);
}
// IBinder.DeathRecipient
@Override
@@ -251,33 +272,39 @@ import java.util.concurrent.ConcurrentLi
MediaCrypto crypto = RemoteMediaDrmBridgeStub.getMediaCrypto(drmStubId);
if (DEBUG) {
boolean hasCrypto = crypto != null;
Log.d(LOGTAG, "configure mediacodec with crypto(" + hasCrypto + ") / Id :" + drmStubId);
}
codec.setCallbacks(new Callbacks(mCallbacks), null);
-
// Video decoder should config with adaptive playback capability.
if (surface != null) {
mIsAdaptivePlaybackSupported = codec.isAdaptivePlaybackSupported(
fmt.getString(MediaFormat.KEY_MIME));
if (mIsAdaptivePlaybackSupported) {
if (DEBUG) Log.d(LOGTAG, "codec supports adaptive playback = " + mIsAdaptivePlaybackSupported);
// TODO: may need to find a way to not use hard code to decide the max w/h.
fmt.setInteger(MediaFormat.KEY_MAX_WIDTH, 1920);
fmt.setInteger(MediaFormat.KEY_MAX_HEIGHT, 1080);
}
}
codec.configure(fmt, surface, crypto, flags);
mCodec = codec;
mInputProcessor = new InputProcessor();
mSamplePool = new SamplePool(codecName);
+
+ if (crypto != null && AppConstants.Versions.preMarshmallow) {
+ mDrmStubId = drmStubId;
+ mCodec.setDrmStubId(drmStubId);
+ RemoteMediaDrmBridgeStub.setupKeyAddedCallback(drmStubId,
+ new CodecKeyWaiter(mCodec));
+ }
if (DEBUG) Log.d(LOGTAG, codec.toString() + " created");
return true;
} catch (Exception e) {
if (DEBUG) Log.d(LOGTAG, "FAIL: cannot create codec -- " + codecName);
e.printStackTrace();
return false;
}
}
--- 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 org.mozilla.gecko.util.MediaCodecUtil;
public interface GeckoMediaDrm {
public interface Callbacks {
void onSessionCreated(int createSessionToken,
int promiseId,
byte[] sessionId,
byte[] request);
void onSessionUpdated(int promiseId, byte[] sessionId);
@@ -28,11 +29,12 @@ public interface GeckoMediaDrm {
int promiseId,
String initDataType,
byte[] initData);
void updateSession(int promiseId, String sessionId, byte[] response);
void closeSession(int promiseId, String sessionId);
void release();
MediaCrypto getMediaCrypto();
+ void setupKeyAddedCallback(MediaCodecUtil.KeyWaiter waiter);
void notifyKeyNeeded();
boolean waitForKey();
}
--- a/mobile/android/base/java/org/mozilla/gecko/media/GeckoMediaDrmBridgeV21.java
+++ b/mobile/android/base/java/org/mozilla/gecko/media/GeckoMediaDrmBridgeV21.java
@@ -1,26 +1,28 @@
/* 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.util.MediaCodecUtil;
import java.lang.*;
import java.io.BufferedReader;
import java.io.IOException;
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.ArrayList;
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,16 +47,18 @@ 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 ArrayList<MediaCodecUtil.KeyWaiter> mKeyWaiters =
+ new ArrayList<MediaCodecUtil.KeyWaiter>();
private boolean keyNeeded = false;
private HashSet<String> mOngoingSessionIds = new HashSet<String>();
private MediaCrypto mCrypto;
protected MediaDrm mDrm;
protected GeckoMediaDrm.Callbacks mCallbacks;
@@ -92,16 +96,19 @@ public class GeckoMediaDrmBridgeV21 impl
}
}
private void notifyKeyAdded(String sessionId) {
if (DEBUG) Log.d(LOGTAG, "notifyKeyAdded() ");
boolean deleted = mOngoingSessionIds.remove(sessionId);
if (deleted && mOngoingSessionIds.isEmpty()) {
keyNeeded = false;
+ for (MediaCodecUtil.KeyWaiter waiter : mKeyWaiters) {
+ waiter.onKeyAdded();
+ }
}
}
@SuppressLint("WrongConstant")
private void configureVendorSpecificProperty() {
assertTrue(mDrm != null);
// Support L3 for now
mDrm.setPropertyString("securityLevel", "L3");
@@ -290,16 +297,23 @@ public class GeckoMediaDrmBridgeV21 impl
@Override
public MediaCrypto getMediaCrypto() {
if (DEBUG) Log.d(LOGTAG, "getMediaCrypto()");
return mCrypto;
}
@Override
+ public void setupKeyAddedCallback(MediaCodecUtil.KeyWaiter callback) {
+ assertTrue(callback != null);
+ if (DEBUG) Log.d(LOGTAG, "setupKeyAddedCallback()");
+ mKeyWaiters.add(callback);
+ }
+
+ @Override
public synchronized void notifyKeyNeeded() {
if (DEBUG) Log.d(LOGTAG, "notifyKeyNeeded()");
keyNeeded = true;
}
@Override
public synchronized boolean waitForKey() {
if (DEBUG) Log.d(LOGTAG, "waitForKey : " + keyNeeded);
--- a/mobile/android/base/java/org/mozilla/gecko/media/GeckoMediaDrmBridgeV23.java
+++ b/mobile/android/base/java/org/mozilla/gecko/media/GeckoMediaDrmBridgeV23.java
@@ -1,13 +1,14 @@
/* 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.util.MediaCodecUtil;
import android.annotation.TargetApi;
import android.media.DeniedByServerException;
import android.media.NotProvisionedException;
import static android.os.Build.VERSION_CODES.M;
import android.media.MediaDrm;
import android.util.Log;
@@ -64,20 +65,26 @@ public class GeckoMediaDrmBridgeV23 exte
protected void onSessionMessage(byte[] sessionId,
int sessionMessageType,
byte[] request) {
assertTrue(mCallbacks != null);
mCallbacks.onSessionMessage(sessionId, sessionMessageType, request);
}
@Override
+ public void setupKeyAddedCallback(MediaCodecUtil.KeyWaiter callback) {
+ // Should not be used starting from Android M.
+ assertTrue(false);
+ }
+
+ @Override
public synchronized void notifyKeyNeeded() {
- // Should not be used after Android M.
+ // Should not be used starting from Android M.
assertTrue(false);
}
@Override
public synchronized boolean waitForKey() {
- // Should not be used after Android M.
+ // Should not be used starting from Android M.
assertTrue(false);
return false;
}
}
--- a/mobile/android/base/java/org/mozilla/gecko/media/JellyBeanAsyncCodec.java
+++ b/mobile/android/base/java/org/mozilla/gecko/media/JellyBeanAsyncCodec.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 org.mozilla.gecko.util.HardwareCodecCapabilityUtils;
+import org.mozilla.gecko.util.MediaCodecUtil;
import android.media.MediaCodec;
import android.media.MediaCrypto;
import android.media.MediaFormat;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
@@ -21,18 +22,16 @@ import java.nio.ByteBuffer;
// Implement async API using MediaCodec sync mode (API v16).
// This class uses internal worker thread/handler (mBufferPoller) to poll
// input and output buffer and notifies the client through callbacks.
final class JellyBeanAsyncCodec implements AsyncCodec {
private static final String LOGTAG = "GeckoAsyncCodecAPIv16";
private static final boolean DEBUG = false;
- private static final int ERROR_CODEC = -10000;
-
private abstract class CancelableHandler extends Handler {
private static final int MSG_CANCELLATION = 0x434E434C; // 'CNCL'
protected CancelableHandler(Looper looper) {
super(looper);
}
protected void cancel() {
@@ -199,17 +198,17 @@ final class JellyBeanAsyncCodec implemen
case MSG_POLL_OUTPUT_BUFFERS:
pollOutputBuffer();
break;
default:
return false;
}
} catch (IllegalStateException e) {
e.printStackTrace();
- mCallbackSender.notifyError(ERROR_CODEC);
+ mCallbackSender.notifyError(MediaCodecUtil.CODEC_STATUS_ERROR);
}
return true;
}
private void pollInputBuffer() {
int result = mCodec.dequeueInputBuffer(DEQUEUE_TIMEOUT_US);
if (result >= 0) {
@@ -253,16 +252,21 @@ final class JellyBeanAsyncCodec implemen
private ByteBuffer[] mOutputBuffers;
private AsyncCodec.Callbacks mCallbacks;
private CallbackSender mCallbackSender;
private BufferPoller mBufferPoller;
private volatile boolean mInputEnded;
private volatile boolean mOutputEnded;
+ // A lock to block the input procedure when encountering no-key error.
+ final Object waitForKeyLock = new Object();
+ private boolean waitForKey = false;
+ private String mDrmStubId;
+
// Must be called on a thread with looper.
/* package */ JellyBeanAsyncCodec(String name) throws IOException {
mCodec = MediaCodec.createByCodecName(name);
initBufferPoller(name + " buffer poller");
}
private void initBufferPoller(String name) {
if (mBufferPoller != null) {
@@ -331,17 +335,17 @@ final class JellyBeanAsyncCodec implemen
assertCallbacks();
mInputEnded = (flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0;
try {
mCodec.queueInputBuffer(index, offset, size, presentationTimeUs, flags);
} catch (IllegalStateException e) {
e.printStackTrace();
- mCallbackSender.notifyError(ERROR_CODEC);
+ mCallbackSender.notifyError(MediaCodecUtil.CODEC_STATUS_ERROR);
return;
}
mBufferPoller.schedulePolling(BufferPoller.MSG_POLL_OUTPUT_BUFFERS);
mBufferPoller.schedulePolling(BufferPoller.MSG_POLL_INPUT_BUFFERS);
}
@Override
@@ -349,26 +353,38 @@ final class JellyBeanAsyncCodec implemen
int offset,
MediaCodec.CryptoInfo cryptoInfo,
long presentationTimeUs,
int flags) {
assertCallbacks();
mInputEnded = (flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0;
- try {
- mCodec.queueSecureInputBuffer(index, offset, cryptoInfo, presentationTimeUs, flags);
- } catch (IllegalStateException e) {
- e.printStackTrace();
- mCallbackSender.notifyError(ERROR_CODEC);
+ int res = MediaCodecUtil.queueSecureInputBuffer(mCodec, index, offset, cryptoInfo, presentationTimeUs, flags);
+ if (res != MediaCodecUtil.CODEC_STATUS_OK) {
+ if (DEBUG) Log.d(LOGTAG, "Got MediaCodecUtil Error : " + res);
+ if (res == MediaCodecUtil.CODEC_STATUS_NO_KEY) {
+ synchronized (waitForKeyLock) {
+ try {
+ waitForKey = true;
+ RemoteMediaDrmBridgeStub.notifyKeyNeeded(mDrmStubId);
+ waitForKeyLock.wait();
+ if (DEBUG) Log.d(LOGTAG, "going to queueSecureInputBuffer 2nd time.");
+ MediaCodecUtil.queueSecureInputBuffer(mCodec, index, offset, cryptoInfo, presentationTimeUs, flags);
+ } catch (Exception e) {
+ Log.e(LOGTAG, "Got exception when queueSecureInputBuffer 2nd time.", e);
+ }
+ }
+ } else {
+ mCallbackSender.notifyError(res);
+ }
return;
}
-
+ mBufferPoller.schedulePolling(BufferPoller.MSG_POLL_OUTPUT_BUFFERS);
mBufferPoller.schedulePolling(BufferPoller.MSG_POLL_INPUT_BUFFERS);
- mBufferPoller.schedulePolling(BufferPoller.MSG_POLL_OUTPUT_BUFFERS);
}
@Override
public final void releaseOutputBuffer(int index, boolean render) {
assertCallbacks();
mCodec.releaseOutputBuffer(index, render);
}
@@ -418,16 +434,36 @@ final class JellyBeanAsyncCodec implemen
assertCallbacks();
cancelPendingTasks();
mCallbackSender = null;
mCodec.release();
stopBufferPoller();
}
+ @Override
+ public void setDrmStubId(String drmStubId) {
+ mDrmStubId = drmStubId;
+ }
+
+ @Override
+ public void notifyKeyAdded() {
+ if (DEBUG) Log.d(LOGTAG, "notifyKeyAdded()");
+ synchronized (waitForKeyLock) {
+ try {
+ if (waitForKey) {
+ waitForKey = false;
+ waitForKeyLock.notify();
+ }
+ } catch (Exception e) {
+ Log.e(LOGTAG, "Got exception : ", e);
+ }
+ }
+ }
+
private void stopBufferPoller() {
if (mBufferPoller == null) {
Log.e(LOGTAG, "no initialized poller.");
return;
}
mBufferPoller.getLooper().quit();
mBufferPoller = null;
--- a/mobile/android/base/java/org/mozilla/gecko/media/LocalMediaDrmBridge.java
+++ b/mobile/android/base/java/org/mozilla/gecko/media/LocalMediaDrmBridge.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 org.mozilla.gecko.AppConstants;
+import org.mozilla.gecko.util.MediaCodecUtil;
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;
@@ -156,16 +157,21 @@ final class LocalMediaDrmBridge implemen
@Override
public synchronized MediaCrypto getMediaCrypto() {
if (DEBUG) Log.d(LOGTAG, "getMediaCrypto()");
return mBridge != null ? mBridge.getMediaCrypto() : null;
}
@Override
+ public void setupKeyAddedCallback(MediaCodecUtil.KeyWaiter callback) {
+ assertTrue(false);
+ }
+
+ @Override
public void notifyKeyNeeded() {
assertTrue(mBridge != null);
mBridge.notifyKeyNeeded();
}
@Override
public boolean waitForKey() {
assertTrue(mBridge != null);
--- a/mobile/android/base/java/org/mozilla/gecko/media/MediaDrmProxy.java
+++ b/mobile/android/base/java/org/mozilla/gecko/media/MediaDrmProxy.java
@@ -369,17 +369,16 @@ public final class MediaDrmProxy {
assertTrue(mImpl != null);
if (DEBUG) Log.d(LOGTAG, "waitForKey()");
return mImpl.waitForKey();
}
@WrapForJNI
public static synchronized boolean waitForKey(String stubId) {
assertTrue(AppConstants.Versions.preMarshmallow);
-
for (MediaDrmProxy proxy : sProxyList) {
if (proxy.getStubId().equals(stubId)) {
return proxy.waitForKey();
}
}
if (DEBUG) Log.d(LOGTAG, "ERROR : Cannot find targe DRM stub - " + stubId);
return false;
}
--- 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,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 org.mozilla.gecko.util.MediaCodecUtil;
+
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;
@@ -146,16 +148,22 @@ 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 setupKeyAddedCallback(MediaCodecUtil.KeyWaiter callback) {
+ // Should not be used in this case.
+ assertTrue(false);
+ }
+
+ @Override
public void notifyKeyNeeded() {
// Should not be used in this case.
assertTrue(false);
}
@Override
public boolean waitForKey() {
// Should not be used in this case.
--- a/mobile/android/base/java/org/mozilla/gecko/media/RemoteMediaDrmBridgeStub.java
+++ b/mobile/android/base/java/org/mozilla/gecko/media/RemoteMediaDrmBridgeStub.java
@@ -1,17 +1,19 @@
/* 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 org.mozilla.gecko.util.MediaCodecUtil;
import java.util.ArrayList;
+import java.util.HashSet;
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";
@@ -47,16 +49,34 @@ final class RemoteMediaDrmBridgeStub ext
if (sBridgeStubs.get(i) != null &&
sBridgeStubs.get(i).getId().equals(stubId)) {
return sBridgeStubs.get(i).getMediaCryptoFromBridge();
}
}
return null;
}
+ public static synchronized void setupKeyAddedCallback(String stubId,
+ MediaCodecUtil.KeyWaiter callback) {
+ assertTrue(AppConstants.Versions.preMarshmallow);
+ if (DEBUG) Log.d(LOGTAG, "setupKeyAddedCallback()");
+
+ for (RemoteMediaDrmBridgeStub bridgeStub : sBridgeStubs) {
+ if (bridgeStub.getId().equals(stubId)) {
+ bridgeStub.setupKeyAddedCallback(callback);
+ return;
+ }
+ }
+ }
+
+ private void setupKeyAddedCallback(MediaCodecUtil.KeyWaiter callback) {
+ assertTrue(mBridge != null);
+ mBridge.setupKeyAddedCallback(callback);
+ }
+
public static synchronized void notifyKeyNeeded(String stubId) {
assertTrue(AppConstants.Versions.preMarshmallow);
if (DEBUG) Log.d(LOGTAG, "notifyKeyNeeded()");
for (RemoteMediaDrmBridgeStub bridgeStub : sBridgeStubs) {
if (bridgeStub.getId().equals(stubId)) {
bridgeStub.notifyKeyNeeded();
return;
@@ -74,16 +94,17 @@ final class RemoteMediaDrmBridgeStub ext
assertTrue(AppConstants.Versions.preMarshmallow);
if (DEBUG) Log.d(LOGTAG, "waitForKey()");
for (RemoteMediaDrmBridgeStub bridgeStub : sBridgeStubs) {
if (bridgeStub.getId().equals(stubId)) {
return bridgeStub.waitForKey();
}
}
+ if (DEBUG) Log.d(LOGTAG, "waitForKey() - find no stub error !");
return false;
}
private boolean waitForKey() {
assertTrue(mBridge != null);
return mBridge.waitForKey();
}
--- a/mobile/android/base/java/org/mozilla/gecko/util/MediaCodecUtil.java
+++ b/mobile/android/base/java/org/mozilla/gecko/util/MediaCodecUtil.java
@@ -38,9 +38,13 @@ public final class MediaCodecUtil {
return CODEC_STATUS_NO_KEY;
}
return CODEC_STATUS_ERROR;
} catch (Exception e) {
return CODEC_STATUS_ERROR;
}
return CODEC_STATUS_OK;
}
+
+ public interface KeyWaiter {
+ public void onKeyAdded();
+ }
}