зеркало из https://github.com/mozilla/pjs.git
Bug 702883 - Use a native solution for locking/unlocking plugin surfaces
This commit is contained in:
Родитель
fb55813472
Коммит
0d687e9030
|
@ -36,152 +36,212 @@
|
|||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "assert.h"
|
||||
#include "ANPBase.h"
|
||||
#include <dlfcn.h>
|
||||
#include <android/log.h>
|
||||
#include "AndroidBridge.h"
|
||||
#include "ANPBase.h"
|
||||
|
||||
#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args)
|
||||
#define ASSIGN(obj, name) (obj)->name = anp_surface_##name
|
||||
|
||||
#define CLEAR_EXCEPTION(env) if (env->ExceptionOccurred()) env->ExceptionClear();
|
||||
|
||||
// Copied from Android headers
|
||||
enum {
|
||||
PIXEL_FORMAT_RGBA_8888 = 1,
|
||||
PIXEL_FORMAT_RGB_565 = 4,
|
||||
};
|
||||
|
||||
struct SurfaceInfo {
|
||||
uint32_t w;
|
||||
uint32_t h;
|
||||
uint32_t s;
|
||||
uint32_t usage;
|
||||
uint32_t format;
|
||||
unsigned char* bits;
|
||||
uint32_t reserved[2];
|
||||
};
|
||||
|
||||
// used to cache JNI method and field IDs for Surface Objects
|
||||
static struct ANPSurfaceInterfaceJavaGlue {
|
||||
bool initialized;
|
||||
jclass geckoAppShellClass;
|
||||
jclass lockInfoCls;
|
||||
jmethodID lockSurfaceANP;
|
||||
jmethodID jUnlockSurfaceANP;
|
||||
jfieldID jDirtyTop;
|
||||
jfieldID jDirtyLeft;
|
||||
jfieldID jDirtyBottom;
|
||||
jfieldID jDirtyRight;
|
||||
jfieldID jFormat;
|
||||
jfieldID jWidth ;
|
||||
jfieldID jHeight;
|
||||
jfieldID jBuffer;
|
||||
bool initialized;
|
||||
jmethodID getSurfaceHolder;
|
||||
jmethodID getSurface;
|
||||
jfieldID surfacePointer;
|
||||
} gSurfaceJavaGlue;
|
||||
|
||||
#define getClassGlobalRef(env, cname) \
|
||||
(jClass = jclass(env->NewGlobalRef(env->FindClass(cname))))
|
||||
static struct ANPSurfaceFunctions {
|
||||
bool initialized;
|
||||
|
||||
static void init(JNIEnv* env) {
|
||||
if (gSurfaceJavaGlue.initialized)
|
||||
return;
|
||||
|
||||
gSurfaceJavaGlue.geckoAppShellClass = mozilla::AndroidBridge::GetGeckoAppShellClass();
|
||||
|
||||
jmethodID getClass = env->GetStaticMethodID(gSurfaceJavaGlue.geckoAppShellClass,
|
||||
"getSurfaceLockInfoClass",
|
||||
"()Ljava/lang/Class;");
|
||||
int (* lock)(void*, SurfaceInfo*, void*);
|
||||
int (* unlockAndPost)(void*);
|
||||
} gSurfaceFunctions;
|
||||
|
||||
gSurfaceJavaGlue.lockInfoCls = (jclass) env->NewGlobalRef(env->CallStaticObjectMethod(gSurfaceJavaGlue.geckoAppShellClass, getClass));
|
||||
|
||||
gSurfaceJavaGlue.jDirtyTop = env->GetFieldID(gSurfaceJavaGlue.lockInfoCls, "dirtyTop", "I");
|
||||
gSurfaceJavaGlue.jDirtyLeft = env->GetFieldID(gSurfaceJavaGlue.lockInfoCls, "dirtyLeft", "I");
|
||||
gSurfaceJavaGlue.jDirtyBottom = env->GetFieldID(gSurfaceJavaGlue.lockInfoCls, "dirtyBottom", "I");
|
||||
gSurfaceJavaGlue.jDirtyRight = env->GetFieldID(gSurfaceJavaGlue.lockInfoCls, "dirtyRight", "I");
|
||||
static inline void* getSurface(JNIEnv* env, jobject view) {
|
||||
if (!env || !view) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gSurfaceJavaGlue.jFormat = env->GetFieldID(gSurfaceJavaGlue.lockInfoCls, "format", "I");
|
||||
gSurfaceJavaGlue.jWidth = env->GetFieldID(gSurfaceJavaGlue.lockInfoCls, "width", "I");
|
||||
gSurfaceJavaGlue.jHeight = env->GetFieldID(gSurfaceJavaGlue.lockInfoCls, "height", "I");
|
||||
if (!gSurfaceJavaGlue.initialized) {
|
||||
|
||||
gSurfaceJavaGlue.jBuffer = env->GetFieldID(gSurfaceJavaGlue.lockInfoCls, "buffer", "Ljava/nio/Buffer;");
|
||||
gSurfaceJavaGlue.lockSurfaceANP = env->GetStaticMethodID(gSurfaceJavaGlue.geckoAppShellClass, "lockSurfaceANP", "(Landroid/view/SurfaceView;IIII)Lorg/mozilla/gecko/SurfaceLockInfo;");
|
||||
gSurfaceJavaGlue.jUnlockSurfaceANP = env->GetStaticMethodID(gSurfaceJavaGlue.geckoAppShellClass, "unlockSurfaceANP", "(Landroid/view/SurfaceView;)V");
|
||||
gSurfaceJavaGlue.initialized = true;
|
||||
jclass surfaceViewClass = env->FindClass("android/view/SurfaceView");
|
||||
gSurfaceJavaGlue.getSurfaceHolder = env->GetMethodID(surfaceViewClass, "getHolder", "()Landroid/view/SurfaceHolder;");
|
||||
|
||||
jclass surfaceHolderClass = env->FindClass("android/view/SurfaceHolder");
|
||||
gSurfaceJavaGlue.getSurface = env->GetMethodID(surfaceHolderClass, "getSurface", "()Landroid/view/Surface;");
|
||||
|
||||
jclass surfaceClass = env->FindClass("android/view/Surface");
|
||||
gSurfaceJavaGlue.surfacePointer = env->GetFieldID(surfaceClass,
|
||||
"mSurfacePointer", "I");
|
||||
|
||||
if (!gSurfaceJavaGlue.surfacePointer) {
|
||||
CLEAR_EXCEPTION(env);
|
||||
|
||||
// It was something else in 2.2.
|
||||
gSurfaceJavaGlue.surfacePointer = env->GetFieldID(surfaceClass,
|
||||
"mSurface", "I");
|
||||
|
||||
if (!gSurfaceJavaGlue.surfacePointer) {
|
||||
CLEAR_EXCEPTION(env);
|
||||
|
||||
// And something else in 2.3+
|
||||
gSurfaceJavaGlue.surfacePointer = env->GetFieldID(surfaceClass,
|
||||
"mNativeSurface", "I");
|
||||
|
||||
CLEAR_EXCEPTION(env);
|
||||
}
|
||||
}
|
||||
|
||||
if (!gSurfaceJavaGlue.surfacePointer) {
|
||||
LOG("Failed to acquire surface pointer");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
env->DeleteLocalRef(surfaceClass);
|
||||
env->DeleteLocalRef(surfaceViewClass);
|
||||
env->DeleteLocalRef(surfaceHolderClass);
|
||||
|
||||
gSurfaceJavaGlue.initialized = (gSurfaceJavaGlue.surfacePointer != NULL);
|
||||
}
|
||||
|
||||
jobject holder = env->CallObjectMethod(view, gSurfaceJavaGlue.getSurfaceHolder);
|
||||
jobject surface = env->CallObjectMethod(holder, gSurfaceJavaGlue.getSurface);
|
||||
jint surfacePointer = env->GetIntField(surface, gSurfaceJavaGlue.surfacePointer);
|
||||
|
||||
env->DeleteLocalRef(holder);
|
||||
env->DeleteLocalRef(surface);
|
||||
|
||||
return (void*)surfacePointer;
|
||||
}
|
||||
|
||||
static bool anp_lock(JNIEnv* env, jobject surfaceView, ANPBitmap* bitmap, ANPRectI* dirtyRect) {
|
||||
LOG("%s", __PRETTY_FUNCTION__);
|
||||
static ANPBitmapFormat convertPixelFormat(int32_t format) {
|
||||
switch (format) {
|
||||
case PIXEL_FORMAT_RGBA_8888: return kRGBA_8888_ANPBitmapFormat;
|
||||
case PIXEL_FORMAT_RGB_565: return kRGB_565_ANPBitmapFormat;
|
||||
default: return kUnknown_ANPBitmapFormat;
|
||||
}
|
||||
}
|
||||
|
||||
static int bytesPerPixel(int32_t format) {
|
||||
switch (format) {
|
||||
case PIXEL_FORMAT_RGBA_8888: return 4;
|
||||
case PIXEL_FORMAT_RGB_565: return 2;
|
||||
default: return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static bool init() {
|
||||
if (gSurfaceFunctions.initialized)
|
||||
return true;
|
||||
|
||||
void* handle = dlopen("/system/lib/libsurfaceflinger_client.so", RTLD_LAZY);
|
||||
|
||||
if (!handle) {
|
||||
LOG("Failed to open libsurfaceflinger_client.so");
|
||||
return false;
|
||||
}
|
||||
|
||||
gSurfaceFunctions.lock = (int (*)(void*, SurfaceInfo*, void*))dlsym(handle, "_ZN7android7Surface4lockEPNS0_11SurfaceInfoEPNS_6RegionEb");
|
||||
gSurfaceFunctions.unlockAndPost = (int (*)(void*))dlsym(handle, "_ZN7android7Surface13unlockAndPostEv");
|
||||
|
||||
gSurfaceFunctions.initialized = (gSurfaceFunctions.lock && gSurfaceFunctions.unlockAndPost);
|
||||
LOG("Initialized? %d\n", gSurfaceFunctions.initialized);
|
||||
return gSurfaceFunctions.initialized;
|
||||
}
|
||||
|
||||
static bool anp_surface_lock(JNIEnv* env, jobject surfaceView, ANPBitmap* bitmap, ANPRectI* dirtyRect) {
|
||||
if (!bitmap || !surfaceView) {
|
||||
LOG("%s, null bitmap or surface, exiting", __PRETTY_FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
init(env);
|
||||
void* surface = getSurface(env, surfaceView);
|
||||
|
||||
if (!bitmap || !surface) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!init()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SurfaceInfo info;
|
||||
int err = gSurfaceFunctions.lock(surface, &info, NULL);
|
||||
if (err < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
jvalue args[5];
|
||||
args[0].l = surfaceView;
|
||||
if (dirtyRect) {
|
||||
args[1].i = dirtyRect->top;
|
||||
args[2].i = dirtyRect->left;
|
||||
args[3].i = dirtyRect->bottom;
|
||||
args[4].i = dirtyRect->right;
|
||||
LOG("dirty rect: %d, %d, %d, %d", dirtyRect->top, dirtyRect->left, dirtyRect->bottom, dirtyRect->right);
|
||||
// We can't lock the specific region, so we must expand the dirty rect
|
||||
// to be the whole surface
|
||||
dirtyRect->left = dirtyRect->top = 0;
|
||||
dirtyRect->right = info.w;
|
||||
dirtyRect->bottom = info.h;
|
||||
}
|
||||
|
||||
int bpr = info.s * bytesPerPixel(info.format);
|
||||
|
||||
bitmap->format = convertPixelFormat(info.format);
|
||||
bitmap->width = info.w;
|
||||
bitmap->height = info.h;
|
||||
bitmap->rowBytes = bpr;
|
||||
|
||||
if (info.w > 0 && info.h > 0) {
|
||||
bitmap->baseAddr = info.bits;
|
||||
} else {
|
||||
args[1].i = args[2].i = args[3].i = args[4].i = 0;
|
||||
}
|
||||
|
||||
jobject info = env->CallStaticObjectMethod(gSurfaceJavaGlue.geckoAppShellClass,
|
||||
gSurfaceJavaGlue.lockSurfaceANP,
|
||||
surfaceView, args[1].i, args[2].i, args[3].i, args[4].i);
|
||||
|
||||
LOG("info: %p", info);
|
||||
if (!info)
|
||||
return false;
|
||||
|
||||
// the surface may have expanded the dirty region so we must to pass that
|
||||
// information back to the plugin.
|
||||
if (dirtyRect) {
|
||||
dirtyRect->left = env->GetIntField(info, gSurfaceJavaGlue.jDirtyLeft);
|
||||
dirtyRect->right = env->GetIntField(info, gSurfaceJavaGlue.jDirtyRight);
|
||||
dirtyRect->top = env->GetIntField(info, gSurfaceJavaGlue.jDirtyTop);
|
||||
dirtyRect->bottom = env->GetIntField(info, gSurfaceJavaGlue.jDirtyBottom);
|
||||
LOG("dirty rect: %d, %d, %d, %d", dirtyRect->top, dirtyRect->left, dirtyRect->bottom, dirtyRect->right);
|
||||
}
|
||||
|
||||
bitmap->width = env->GetIntField(info, gSurfaceJavaGlue.jWidth);
|
||||
bitmap->height = env->GetIntField(info, gSurfaceJavaGlue.jHeight);
|
||||
|
||||
int format = env->GetIntField(info, gSurfaceJavaGlue.jFormat);
|
||||
|
||||
// format is PixelFormat
|
||||
if (format & 0x00000001) {
|
||||
bitmap->format = kRGBA_8888_ANPBitmapFormat;
|
||||
bitmap->rowBytes = bitmap->width * 4;
|
||||
}
|
||||
else if (format & 0x00000004) {
|
||||
bitmap->format = kRGB_565_ANPBitmapFormat;
|
||||
bitmap->rowBytes = bitmap->width * 2;
|
||||
}
|
||||
else {
|
||||
LOG("format from glue is unknown %d\n", format);
|
||||
bitmap->baseAddr = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
jobject buf = env->GetObjectField(info, gSurfaceJavaGlue.jBuffer);
|
||||
bitmap->baseAddr = env->GetDirectBufferAddress(buf);
|
||||
|
||||
LOG("format: %d, width: %d, height: %d", bitmap->format, bitmap->width, bitmap->height);
|
||||
env->DeleteLocalRef(info);
|
||||
env->DeleteLocalRef(buf);
|
||||
return ( bitmap->width > 0 && bitmap->height > 0 );
|
||||
return true;
|
||||
}
|
||||
|
||||
static void anp_unlock(JNIEnv* env, jobject surfaceView) {
|
||||
LOG("%s", __PRETTY_FUNCTION__);
|
||||
|
||||
static void anp_surface_unlock(JNIEnv* env, jobject surfaceView) {
|
||||
if (!surfaceView) {
|
||||
LOG("null surface, exiting %s", __PRETTY_FUNCTION__);
|
||||
return;
|
||||
}
|
||||
|
||||
init(env);
|
||||
env->CallStaticVoidMethod(gSurfaceJavaGlue.geckoAppShellClass, gSurfaceJavaGlue.jUnlockSurfaceANP, surfaceView);
|
||||
LOG("returning from %s", __PRETTY_FUNCTION__);
|
||||
if (!init()) {
|
||||
return;
|
||||
}
|
||||
|
||||
void* surface = getSurface(env, surfaceView);
|
||||
|
||||
if (!surface) {
|
||||
return;
|
||||
}
|
||||
|
||||
gSurfaceFunctions.unlockAndPost(surface);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define ASSIGN(obj, name) (obj)->name = anp_##name
|
||||
|
||||
void InitSurfaceInterface(ANPSurfaceInterfaceV0 *i) {
|
||||
|
||||
void InitSurfaceInterface(ANPSurfaceInterfaceV0* i) {
|
||||
ASSIGN(i, lock);
|
||||
ASSIGN(i, unlock);
|
||||
|
||||
// setup the java glue struct
|
||||
gSurfaceJavaGlue.initialized = false;
|
||||
|
||||
// setup the function struct
|
||||
gSurfaceFunctions.initialized = false;
|
||||
}
|
||||
|
|
|
@ -68,6 +68,7 @@ CPPSRCS += ANPAudio.cpp \
|
|||
|
||||
LOCAL_INCLUDES += \
|
||||
-I$(topsrcdir)/dom/plugins/base \
|
||||
-I$(topsrcdir)/dom/plugins/base/android/include \
|
||||
$(MOZ_CAIRO_CFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
|
|
|
@ -1290,105 +1290,6 @@ public class GeckoAppShell
|
|||
return null;
|
||||
}
|
||||
|
||||
static HashMap<SurfaceView, SurfaceLockInfo> sSufaceMap = new HashMap<SurfaceView, SurfaceLockInfo>();
|
||||
|
||||
public static void lockSurfaceANP()
|
||||
{
|
||||
Log.i("GeckoAppShell", "other lockSurfaceANP");
|
||||
}
|
||||
|
||||
public static org.mozilla.gecko.SurfaceLockInfo lockSurfaceANP(android.view.SurfaceView sview, int top, int left, int bottom, int right)
|
||||
{
|
||||
Log.i("GeckoAppShell", "real lockSurfaceANP " + sview + ", " + top + ", " + left + ", " + bottom + ", " + right);
|
||||
if (sview == null)
|
||||
return null;
|
||||
|
||||
int format = -1;
|
||||
try {
|
||||
Field privateFormatField = SurfaceView.class.getDeclaredField("mFormat");
|
||||
privateFormatField.setAccessible(true);
|
||||
format = privateFormatField.getInt(sview);
|
||||
} catch (Exception e) {
|
||||
Log.i("GeckoAppShell", "mFormat is not a field of sview: ", e);
|
||||
}
|
||||
|
||||
int n = 0;
|
||||
if (format == PixelFormat.RGB_565)
|
||||
n = 2;
|
||||
else if (format == PixelFormat.RGBA_8888)
|
||||
n = 4;
|
||||
|
||||
if (n == 0)
|
||||
return null;
|
||||
|
||||
SurfaceLockInfo info = sSufaceMap.get(sview);
|
||||
if (info == null) {
|
||||
info = new SurfaceLockInfo();
|
||||
sSufaceMap.put(sview, info);
|
||||
}
|
||||
|
||||
Rect r = new Rect(left, top, right, bottom);
|
||||
|
||||
info.canvas = sview.getHolder().lockCanvas(r);
|
||||
int bufSizeRequired = info.canvas.getWidth() * info.canvas.getHeight() * n;
|
||||
Log.i("GeckoAppShell", "lockSurfaceANP - bufSizeRequired: " + n + " " + info.canvas.getHeight() + " " + info.canvas.getWidth());
|
||||
|
||||
if (info.width != info.canvas.getWidth() || info.height != info.canvas.getHeight() || info.buffer == null || info.buffer.capacity() < bufSizeRequired) {
|
||||
info.width = info.canvas.getWidth();
|
||||
info.height = info.canvas.getHeight();
|
||||
|
||||
// XXX Bitmaps instead of ByteBuffer
|
||||
info.buffer = ByteBuffer.allocateDirect(bufSizeRequired); //leak
|
||||
Log.i("GeckoAppShell", "!!!!!!!!!!! lockSurfaceANP - Allocating buffer! " + bufSizeRequired);
|
||||
|
||||
}
|
||||
|
||||
info.canvas.drawColor(Color.WHITE, PorterDuff.Mode.CLEAR);
|
||||
|
||||
info.format = format;
|
||||
info.dirtyTop = top;
|
||||
info.dirtyBottom = bottom;
|
||||
info.dirtyLeft = left;
|
||||
info.dirtyRight = right;
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
public static void unlockSurfaceANP(SurfaceView sview) {
|
||||
SurfaceLockInfo info = sSufaceMap.get(sview);
|
||||
|
||||
int n = 0;
|
||||
Bitmap.Config config;
|
||||
if (info.format == PixelFormat.RGB_565) {
|
||||
n = 2;
|
||||
config = Bitmap.Config.RGB_565;
|
||||
} else {
|
||||
n = 4;
|
||||
config = Bitmap.Config.ARGB_8888;
|
||||
}
|
||||
|
||||
Log.i("GeckoAppShell", "unlockSurfaceANP: " + (info.width * info.height * n));
|
||||
|
||||
Bitmap bm = Bitmap.createBitmap(info.width, info.height, config);
|
||||
bm.copyPixelsFromBuffer(info.buffer);
|
||||
info.canvas.drawBitmap(bm, 0, 0, null);
|
||||
sview.getHolder().unlockCanvasAndPost(info.canvas);
|
||||
}
|
||||
|
||||
public static Class getSurfaceLockInfoClass() {
|
||||
Log.i("GeckoAppShell", "class name: " + SurfaceLockInfo.class.getName());
|
||||
return SurfaceLockInfo.class;
|
||||
}
|
||||
|
||||
public static Method getSurfaceLockMethod() {
|
||||
Method[] m = GeckoAppShell.class.getMethods();
|
||||
for (int i = 0; i < m.length; i++) {
|
||||
if (m[i].getName().equals("lockSurfaceANP"))
|
||||
return m[i];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static native void executeNextRunnable();
|
||||
|
||||
static class GeckoRunnableCallback implements Runnable {
|
||||
|
|
Загрузка…
Ссылка в новой задаче