mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-25 05:41:12 +00:00
Bug 1297969 - Extract class RemoteManager from CodecProxy.java to RemoteManager.java for further usage. r=jolin
MozReview-Commit-ID: Hqm6ZEzv3v1 --HG-- extra : transplant_source : f%B6%AB%92%A6%D8%B1%CA%9D%98%C9%BC1%0Em%7EJ%24c%0A
This commit is contained in:
parent
71d43e7c12
commit
01f39797f5
@ -16,24 +16,16 @@ import android.media.MediaCodec;
|
||||
import android.media.MediaCodec.BufferInfo;
|
||||
import android.media.MediaFormat;
|
||||
import android.os.DeadObjectException;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
import android.view.Surface;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
// Proxy class of ICodec binder.
|
||||
public final class CodecProxy {
|
||||
private static final String LOGTAG = "GeckoRemoteCodecProxy";
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
private static final RemoteManager sRemoteManager = new RemoteManager();
|
||||
|
||||
private ICodec mRemote;
|
||||
private FormatParam mFormat;
|
||||
private Surface mOutputSurface;
|
||||
@ -97,167 +89,13 @@ public final class CodecProxy {
|
||||
}
|
||||
}
|
||||
|
||||
private static final class RemoteManager implements IBinder.DeathRecipient {
|
||||
private List<CodecProxy> mProxies = new LinkedList<CodecProxy>();
|
||||
private ICodecManager mRemote;
|
||||
private volatile CountDownLatch mConnectionLatch;
|
||||
private final ServiceConnection mConnection = new ServiceConnection() {
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||
if (DEBUG) Log.d(LOGTAG, "service connected");
|
||||
try {
|
||||
service.linkToDeath(RemoteManager.this, 0);
|
||||
} catch (RemoteException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
mRemote = ICodecManager.Stub.asInterface(service);
|
||||
if (mConnectionLatch != null) {
|
||||
mConnectionLatch.countDown();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a connection to the Service has been lost. This typically
|
||||
* happens when the process hosting the service has crashed or been killed.
|
||||
* This does <em>not</em> remove the ServiceConnection itself -- this
|
||||
* binding to the service will remain active, and you will receive a call
|
||||
* to {@link #onServiceConnected} when the Service is next running.
|
||||
*
|
||||
* @param name The concrete component name of the service whose
|
||||
* connection has been lost.
|
||||
*/
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName name) {
|
||||
if (DEBUG) Log.d(LOGTAG, "service disconnected");
|
||||
mRemote.asBinder().unlinkToDeath(RemoteManager.this, 0);
|
||||
mRemote = null;
|
||||
if (mConnectionLatch != null) {
|
||||
mConnectionLatch.countDown();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public synchronized boolean init() {
|
||||
if (mRemote != null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (DEBUG) Log.d(LOGTAG, "init remote manager " + this);
|
||||
Context appCtxt = GeckoAppShell.getApplicationContext();
|
||||
if (DEBUG) Log.d(LOGTAG, "ctxt=" + appCtxt);
|
||||
appCtxt.bindService(new Intent(appCtxt, CodecManager.class),
|
||||
mConnection, Context.BIND_AUTO_CREATE);
|
||||
if (!waitConnection()) {
|
||||
appCtxt.unbindService(mConnection);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean waitConnection() {
|
||||
boolean ok = false;
|
||||
|
||||
mConnectionLatch = new CountDownLatch(1);
|
||||
try {
|
||||
int retryCount = 0;
|
||||
while (retryCount < 5) {
|
||||
if (DEBUG) Log.d(LOGTAG, "waiting for connection latch:" + mConnectionLatch);
|
||||
mConnectionLatch.await(1, TimeUnit.SECONDS);
|
||||
if (mConnectionLatch.getCount() == 0) {
|
||||
break;
|
||||
}
|
||||
Log.w(LOGTAG, "Creator not connected in 1s. Try again.");
|
||||
retryCount++;
|
||||
}
|
||||
ok = true;
|
||||
} catch (InterruptedException e) {
|
||||
Log.e(LOGTAG, "service not connected in 5 seconds. Stop waiting.");
|
||||
e.printStackTrace();
|
||||
}
|
||||
mConnectionLatch = null;
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
public synchronized CodecProxy createCodec(MediaFormat format, Surface surface, Callbacks callbacks) {
|
||||
try {
|
||||
ICodec remote = mRemote.createCodec();
|
||||
CodecProxy proxy = new CodecProxy(format, surface, callbacks);
|
||||
if (proxy.init(remote)) {
|
||||
mProxies.add(proxy);
|
||||
return proxy;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void binderDied() {
|
||||
Log.e(LOGTAG, "remote codec is dead");
|
||||
handleRemoteDeath();
|
||||
}
|
||||
|
||||
private synchronized void handleRemoteDeath() {
|
||||
// Wait for onServiceDisconnected()
|
||||
if (!waitConnection()) {
|
||||
notifyError(true);
|
||||
return;
|
||||
}
|
||||
// Restart
|
||||
if (init() && recoverRemoteCodec()) {
|
||||
notifyError(false);
|
||||
} else {
|
||||
notifyError(true);
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void notifyError(boolean fatal) {
|
||||
for (CodecProxy proxy : mProxies) {
|
||||
proxy.mCallbacks.reportError(fatal);
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized boolean recoverRemoteCodec() {
|
||||
if (DEBUG) Log.d(LOGTAG, "recover codec");
|
||||
boolean ok = true;
|
||||
try {
|
||||
for (CodecProxy proxy : mProxies) {
|
||||
ok &= proxy.init(mRemote.createCodec());
|
||||
}
|
||||
return ok;
|
||||
} catch (RemoteException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void releaseCodec(CodecProxy proxy) throws DeadObjectException, RemoteException {
|
||||
proxy.deinit();
|
||||
synchronized (this) {
|
||||
if (mProxies.remove(proxy) && mProxies.isEmpty()) {
|
||||
release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void release() {
|
||||
if (DEBUG) Log.d(LOGTAG, "release remote manager " + this);
|
||||
Context appCtxt = GeckoAppShell.getApplicationContext();
|
||||
mRemote.asBinder().unlinkToDeath(this, 0);
|
||||
mRemote = null;
|
||||
appCtxt.unbindService(mConnection);
|
||||
}
|
||||
}
|
||||
|
||||
@WrapForJNI
|
||||
public static CodecProxy create(MediaFormat format, Surface surface, Callbacks callbacks) {
|
||||
if (!sRemoteManager.init()) {
|
||||
return null;
|
||||
}
|
||||
return sRemoteManager.createCodec(format, surface, callbacks);
|
||||
return RemoteManager.getInstance().createCodec(format, surface, callbacks);
|
||||
}
|
||||
|
||||
public static CodecProxy createCodecProxy(MediaFormat format, Surface surface, Callbacks callbacks) {
|
||||
return new CodecProxy(format, surface, callbacks);
|
||||
}
|
||||
|
||||
private CodecProxy(MediaFormat format, Surface surface, Callbacks callbacks) {
|
||||
@ -266,7 +104,7 @@ public final class CodecProxy {
|
||||
mCallbacks = new CallbacksForwarder(callbacks);
|
||||
}
|
||||
|
||||
private boolean init(ICodec remote) {
|
||||
boolean init(ICodec remote) {
|
||||
try {
|
||||
remote.setCallbacks(mCallbacks);
|
||||
remote.configure(mFormat, mOutputSurface, 0);
|
||||
@ -280,7 +118,7 @@ public final class CodecProxy {
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean deinit() {
|
||||
boolean deinit() {
|
||||
try {
|
||||
mRemote.stop();
|
||||
mRemote.release();
|
||||
@ -338,7 +176,7 @@ public final class CodecProxy {
|
||||
}
|
||||
if (DEBUG) Log.d(LOGTAG, "release " + this);
|
||||
try {
|
||||
sRemoteManager.releaseCodec(this);
|
||||
RemoteManager.getInstance().releaseCodec(this);
|
||||
} catch (DeadObjectException e) {
|
||||
return false;
|
||||
} catch (RemoteException e) {
|
||||
@ -347,4 +185,8 @@ public final class CodecProxy {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public synchronized void reportError(boolean fatal) {
|
||||
mCallbacks.reportError(fatal);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,201 @@
|
||||
/* 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.GeckoAppShell;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.media.MediaFormat;
|
||||
import android.os.DeadObjectException;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.view.Surface;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public final class RemoteManager implements IBinder.DeathRecipient {
|
||||
private static final String LOGTAG = "GeckoRemoteManager";
|
||||
private static final boolean DEBUG = false;
|
||||
private static RemoteManager sRemoteManager = null;
|
||||
|
||||
public synchronized static RemoteManager getInstance() {
|
||||
if (sRemoteManager == null){
|
||||
sRemoteManager = new RemoteManager();
|
||||
}
|
||||
|
||||
sRemoteManager.init();
|
||||
return sRemoteManager;
|
||||
}
|
||||
|
||||
private List<CodecProxy> mProxies = new LinkedList<CodecProxy>();
|
||||
private volatile ICodecManager mRemote;
|
||||
private volatile CountDownLatch mConnectionLatch;
|
||||
private final ServiceConnection mConnection = new ServiceConnection() {
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||
if (DEBUG) Log.d(LOGTAG, "service connected");
|
||||
try {
|
||||
service.linkToDeath(RemoteManager.this, 0);
|
||||
} catch (RemoteException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
mRemote = ICodecManager.Stub.asInterface(service);
|
||||
if (mConnectionLatch != null) {
|
||||
mConnectionLatch.countDown();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a connection to the Service has been lost. This typically
|
||||
* happens when the process hosting the service has crashed or been killed.
|
||||
* This does <em>not</em> remove the ServiceConnection itself -- this
|
||||
* binding to the service will remain active, and you will receive a call
|
||||
* to {@link #onServiceConnected} when the Service is next running.
|
||||
*
|
||||
* @param name The concrete component name of the service whose
|
||||
* connection has been lost.
|
||||
*/
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName name) {
|
||||
if (DEBUG) Log.d(LOGTAG, "service disconnected");
|
||||
mRemote.asBinder().unlinkToDeath(RemoteManager.this, 0);
|
||||
mRemote = null;
|
||||
if (mConnectionLatch != null) {
|
||||
mConnectionLatch.countDown();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private synchronized boolean init() {
|
||||
if (mRemote != null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (DEBUG) Log.d(LOGTAG, "init remote manager " + this);
|
||||
Context appCtxt = GeckoAppShell.getApplicationContext();
|
||||
if (DEBUG) Log.d(LOGTAG, "ctxt=" + appCtxt);
|
||||
appCtxt.bindService(new Intent(appCtxt, CodecManager.class),
|
||||
mConnection, Context.BIND_AUTO_CREATE);
|
||||
if (!waitConnection()) {
|
||||
appCtxt.unbindService(mConnection);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean waitConnection() {
|
||||
boolean ok = false;
|
||||
|
||||
mConnectionLatch = new CountDownLatch(1);
|
||||
try {
|
||||
int retryCount = 0;
|
||||
while (retryCount < 5) {
|
||||
if (DEBUG) Log.d(LOGTAG, "waiting for connection latch:" + mConnectionLatch);
|
||||
mConnectionLatch.await(1, TimeUnit.SECONDS);
|
||||
if (mConnectionLatch.getCount() == 0) {
|
||||
break;
|
||||
}
|
||||
Log.w(LOGTAG, "Creator not connected in 1s. Try again.");
|
||||
retryCount++;
|
||||
}
|
||||
ok = true;
|
||||
} catch (InterruptedException e) {
|
||||
Log.e(LOGTAG, "service not connected in 5 seconds. Stop waiting.");
|
||||
e.printStackTrace();
|
||||
}
|
||||
mConnectionLatch = null;
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
public synchronized CodecProxy createCodec(MediaFormat format,
|
||||
Surface surface,
|
||||
CodecProxy.Callbacks callbacks) {
|
||||
if (mRemote == null) {
|
||||
if (DEBUG) Log.d(LOGTAG, "createCodec failed due to not initialize");
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
ICodec remote = mRemote.createCodec();
|
||||
CodecProxy proxy = CodecProxy.createCodecProxy(format, surface, callbacks);
|
||||
if (proxy.init(remote)) {
|
||||
mProxies.add(proxy);
|
||||
return proxy;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void binderDied() {
|
||||
Log.e(LOGTAG, "remote codec is dead");
|
||||
handleRemoteDeath();
|
||||
}
|
||||
|
||||
private synchronized void handleRemoteDeath() {
|
||||
// Wait for onServiceDisconnected()
|
||||
if (!waitConnection()) {
|
||||
notifyError(true);
|
||||
return;
|
||||
}
|
||||
// Restart
|
||||
if (init() && recoverRemoteCodec()) {
|
||||
notifyError(false);
|
||||
} else {
|
||||
notifyError(true);
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void notifyError(boolean fatal) {
|
||||
for (CodecProxy proxy : mProxies) {
|
||||
proxy.reportError(fatal);
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized boolean recoverRemoteCodec() {
|
||||
if (DEBUG) Log.d(LOGTAG, "recover codec");
|
||||
boolean ok = true;
|
||||
try {
|
||||
for (CodecProxy proxy : mProxies) {
|
||||
ok &= proxy.init(mRemote.createCodec());
|
||||
}
|
||||
return ok;
|
||||
} catch (RemoteException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void releaseCodec(CodecProxy proxy) throws DeadObjectException, RemoteException {
|
||||
if (mRemote == null) {
|
||||
if (DEBUG) Log.d(LOGTAG, "releaseCodec called but not initialized yet");
|
||||
return;
|
||||
}
|
||||
proxy.deinit();
|
||||
synchronized (this) {
|
||||
if (mProxies.remove(proxy) && mProxies.isEmpty()) {
|
||||
release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void release() {
|
||||
if (DEBUG) Log.d(LOGTAG, "release remote manager " + this);
|
||||
Context appCtxt = GeckoAppShell.getApplicationContext();
|
||||
mRemote.asBinder().unlinkToDeath(this, 0);
|
||||
mRemote = null;
|
||||
appCtxt.unbindService(mConnection);
|
||||
}
|
||||
} // RemoteManager
|
@ -508,6 +508,7 @@ gbjar.sources += ['java/org/mozilla/gecko/' + x for x in [
|
||||
'media/FormatParam.java',
|
||||
'media/JellyBeanAsyncCodec.java',
|
||||
'media/MediaControlService.java',
|
||||
'media/RemoteManager.java',
|
||||
'media/Sample.java',
|
||||
'MediaCastingBar.java',
|
||||
'MemoryMonitor.java',
|
||||
|
Loading…
Reference in New Issue
Block a user