mirror of
https://github.com/defold/extension-camera
synced 2025-06-27 18:37:43 +02:00
Work in progress
This commit is contained in:
parent
49f954ae04
commit
972ff2a75c
141
AndroidManifest.xml
Normal file
141
AndroidManifest.xml
Normal file
@ -0,0 +1,141 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- BEGIN_INCLUDE(manifest) -->
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="{{android.package}}"
|
||||
android:versionCode="{{android.version_code}}"
|
||||
android:versionName="{{project.version}}"
|
||||
android:installLocation="auto">
|
||||
|
||||
<uses-feature android:name="android.hardware.camera" />
|
||||
<uses-feature android:name="android.hardware.camera.autofocus" />
|
||||
<uses-feature android:required="true" android:glEsVersion="0x00020000" />
|
||||
<uses-sdk android:minSdkVersion="9" android:targetSdkVersion="23" />
|
||||
<application
|
||||
{{#has-icons?}}
|
||||
android:icon="@drawable/icon"
|
||||
{{/has-icons?}}
|
||||
android:label="{{project.title}}" android:hasCode="true" android:debuggable="false">
|
||||
|
||||
<!-- For Local Notifications -->
|
||||
<receiver android:name="com.defold.push.LocalNotificationReceiver" >
|
||||
</receiver>
|
||||
|
||||
<!-- For GCM (push) -->
|
||||
<meta-data
|
||||
android:name="com.google.android.gms.version"
|
||||
android:value="@integer/google_play_services_version" />
|
||||
|
||||
<!-- For Facebook -->
|
||||
<meta-data android:name="com.facebook.sdk.ApplicationName"
|
||||
android:value="{{project.title}}" />
|
||||
|
||||
<activity android:name="com.dynamo.android.DefoldActivity"
|
||||
android:label="{{project.title}}"
|
||||
android:configChanges="orientation|screenSize|keyboardHidden"
|
||||
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
|
||||
android:screenOrientation="{{orientation-support}}"
|
||||
android:launchMode="singleTask">
|
||||
<meta-data android:name="android.app.lib_name"
|
||||
android:value="{{exe-name}}" />
|
||||
{{#android.push_field_title}}
|
||||
<meta-data
|
||||
android:name="com.defold.push.field_title"
|
||||
android:value="{{android.push_field_title}}" />
|
||||
{{/android.push_field_title}}
|
||||
{{#android.push_field_text}}
|
||||
<meta-data
|
||||
android:name="com.defold.push.field_text"
|
||||
android:value="{{android.push_field_text}}" />
|
||||
{{/android.push_field_text}}
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:name="com.dynamo.android.DispatcherActivity" android:theme="@android:style/Theme.Translucent.NoTitleBar" />
|
||||
<activity android:name="com.facebook.FacebookActivity"
|
||||
android:theme="@android:style/Theme.Translucent.NoTitleBar"
|
||||
android:configChanges="keyboard|keyboardHidden|screenLayout|screenSize|orientation"
|
||||
android:label="{{project.title}}" />
|
||||
<activity android:name="com.defold.iap.IapGooglePlayActivity"
|
||||
android:theme="@android:style/Theme.Translucent.NoTitleBar"
|
||||
android:configChanges="keyboard|keyboardHidden|screenLayout|screenSize|orientation"
|
||||
android:label="IAP">
|
||||
</activity>
|
||||
|
||||
<!-- For Local Notifications -->
|
||||
<activity android:name="com.defold.push.LocalPushDispatchActivity"
|
||||
android:theme="@android:style/Theme.Translucent.NoTitleBar"
|
||||
android:launchMode="singleTask"
|
||||
android:configChanges="keyboardHidden|orientation|screenSize">
|
||||
<intent-filter>
|
||||
<action android:name="com.defold.push.FORWARD" />
|
||||
<category android:name="com.defold.push" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<!-- For GCM (push) -->
|
||||
<activity android:name="com.defold.push.PushDispatchActivity" android:theme="@android:style/Theme.Translucent.NoTitleBar">
|
||||
<intent-filter>
|
||||
<action android:name="com.defold.push.FORWARD" />
|
||||
<category android:name="com.defold.push" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<receiver
|
||||
android:name="com.defold.push.GcmBroadcastReceiver"
|
||||
android:permission="com.google.android.c2dm.permission.SEND" >
|
||||
<intent-filter>
|
||||
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
|
||||
<action android:name="com.defold.push.FORWARD" />
|
||||
<category android:name="com.defold.push" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<!-- For IAC Invocations -->
|
||||
<activity android:name="com.defold.iac.IACActivity"
|
||||
android:theme="@android:style/Theme.Translucent.NoTitleBar"
|
||||
android:launchMode="singleTask"
|
||||
android:configChanges="keyboardHidden|orientation|screenSize">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:scheme="{{android.package}}" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<service android:name="com.defold.adtruth.InstallReceiver"/>
|
||||
<receiver
|
||||
android:name="com.defold.adtruth.InstallReceiver"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="com.android.vending.INSTALL_REFERRER" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<!-- For Amazon IAP -->
|
||||
<receiver android:name="com.amazon.device.iap.ResponseReceiver" >
|
||||
<intent-filter>
|
||||
<action android:name="com.amazon.inapp.purchasing.NOTIFY" android:permission="com.amazon.inapp.purchasing.Permission.NOTIFY" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
</application>
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="com.android.vending.BILLING" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
|
||||
<!-- For GCM (push) -->
|
||||
<!-- NOTE: Package name from actual app here! -->
|
||||
<permission android:name="{{android.package}}.permission.C2D_MESSAGE" android:protectionLevel="signature" />
|
||||
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
|
||||
<!-- NOTE: Package name from actual app here! -->
|
||||
<uses-permission android:name="{{android.package}}.permission.C2D_MESSAGE" />
|
||||
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
|
||||
</manifest>
|
||||
<!-- END_INCLUDE(manifest) -->
|
381
camera/src/android/camera.java
Normal file
381
camera/src/android/camera.java
Normal file
@ -0,0 +1,381 @@
|
||||
package com.defold.android.camera;
|
||||
|
||||
import android.hardware.camera2.CameraManager;
|
||||
import android.hardware.camera2.CameraCharacteristics;
|
||||
import android.hardware.camera2.CameraAccessException;
|
||||
import android.hardware.camera2.CameraCaptureSession;
|
||||
import android.hardware.camera2.CameraDevice;
|
||||
|
||||
import android.hardware.Camera;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
import android.app.Activity;
|
||||
import android.media.ImageReader;
|
||||
import android.view.Surface;
|
||||
import android.view.SurfaceView;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.View;
|
||||
import android.app.Activity;
|
||||
import android.graphics.ImageFormat;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.lang.Runnable;
|
||||
import java.lang.Math;
|
||||
|
||||
|
||||
|
||||
class CameraExtension implements Camera.PreviewCallback {
|
||||
|
||||
private static final String TAG = "defold";
|
||||
|
||||
private static final String[] PERMISSIONS = { Manifest.permission.CAMERA };
|
||||
|
||||
private Camera camera;
|
||||
private Camera.Size pictureSize;
|
||||
private Camera.Size previewSize;
|
||||
private Camera.Parameters parameters;
|
||||
private SurfaceView surfaceView;
|
||||
|
||||
private byte[] previewPixels;
|
||||
private byte[] picturePixels;
|
||||
|
||||
private static CameraExtension instance;
|
||||
|
||||
private CameraExtension() {
|
||||
|
||||
}
|
||||
|
||||
private static CameraExtension getInstance() {
|
||||
if (instance == null) {
|
||||
Log.v(TAG, "No instance, creating");
|
||||
instance = new CameraExtension();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
private static boolean hasAllPermissions(Context context, String... permissions) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && context != null && permissions != null) {
|
||||
for (String permission : permissions) {
|
||||
if (context.checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void requestPermissions(Context context, String... permissions) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
if (!hasAllPermissions(context, permissions)) {
|
||||
((Activity)context).requestPermissions(permissions, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static Camera openCamera(int facing) {
|
||||
Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
|
||||
int cameraCount = Camera.getNumberOfCameras();
|
||||
for (int i = 0; i < cameraCount; i++) {
|
||||
Camera.getCameraInfo(i, cameraInfo);
|
||||
if (cameraInfo.facing == facing) {
|
||||
return Camera.open(i);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Camera openFrontCamera() {
|
||||
return openCamera(Camera.CameraInfo.CAMERA_FACING_FRONT);
|
||||
}
|
||||
|
||||
private static Camera openBackCamera() {
|
||||
return openCamera(Camera.CameraInfo.CAMERA_FACING_BACK);
|
||||
}
|
||||
|
||||
private native void helloworld();
|
||||
|
||||
private native void sendPicture(byte[] picture);
|
||||
|
||||
private native void handleCameraFrame(byte[] cameraFrame, int width, int height);
|
||||
|
||||
//Method from Ketai project!
|
||||
private void decodeYUV420SP(byte[] rgb, byte[] yuv420sp, int width, int height) {
|
||||
final int frameSize = width * height;
|
||||
|
||||
for (int j = 0, yp = 0; j < height; j++) {
|
||||
int uvp = frameSize + (j >> 1) * width;
|
||||
int u = 0;
|
||||
int v = 0;
|
||||
for (int i = 0; i < width; i++, yp++) {
|
||||
int y = (0xff & ((int) yuv420sp[yp])) - 16;
|
||||
if (y < 0) {
|
||||
y = 0;
|
||||
}
|
||||
if ((i & 1) == 0) {
|
||||
v = (0xff & yuv420sp[uvp++]) - 128;
|
||||
u = (0xff & yuv420sp[uvp++]) - 128;
|
||||
}
|
||||
|
||||
int y1192 = 1192 * y;
|
||||
int r = (y1192 + 1634 * v);
|
||||
int g = (y1192 - 833 * v - 400 * u);
|
||||
int b = (y1192 + 2066 * u);
|
||||
|
||||
if (r < 0) {
|
||||
r = 0;
|
||||
}
|
||||
else if (r > 262143) {
|
||||
r = 262143;
|
||||
}
|
||||
|
||||
if (g < 0) {
|
||||
g = 0;
|
||||
}
|
||||
else if (g > 262143) {
|
||||
g = 262143;
|
||||
}
|
||||
|
||||
if (b < 0) {
|
||||
b = 0;
|
||||
}
|
||||
else if (b > 262143) {
|
||||
b = 262143;
|
||||
}
|
||||
|
||||
int pixel = 0xff000000 | ((r << 6) & 0xff0000) | ((g >> 2) & 0xff00) | ((b >> 10) & 0xff);
|
||||
rgb[(yp * 3)+0] = (byte)(pixel & 0x00ff0000);
|
||||
rgb[(yp * 3)+1] = (byte)(pixel & 0x0000ff00);
|
||||
rgb[(yp * 3)+2] = (byte)(pixel & 0x000000ff);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Repeatedly take pictures
|
||||
*/
|
||||
private void takePicture(final Context context) {
|
||||
((Activity)context).runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (camera != null) {
|
||||
Log.v(TAG, "Start preview");
|
||||
surfaceView.setVisibility(View.VISIBLE);
|
||||
camera.startPreview();
|
||||
surfaceView.setVisibility(View.INVISIBLE);
|
||||
|
||||
Log.v(TAG, "Take picture");
|
||||
camera.takePicture(null, null, new Camera.PictureCallback() {
|
||||
@Override
|
||||
public void onPictureTaken(byte[] data, Camera camera) {
|
||||
Log.v(TAG, "onPictureTaken");
|
||||
if (data != null) {
|
||||
sendPicture(data);
|
||||
}
|
||||
else {
|
||||
Log.v(TAG, "onPictureTaken - data is null");
|
||||
}
|
||||
takePicture(context);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPreviewFrame(byte[] data, Camera camera) {
|
||||
Log.v(TAG, "onPreviewFrame " + previewSize.width + "x" + previewSize.height);
|
||||
if (data != null) {
|
||||
handleCameraFrame(data, previewSize.width, previewSize.height);
|
||||
surfaceView.setVisibility(View.VISIBLE);
|
||||
camera.startPreview();
|
||||
surfaceView.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
else {
|
||||
Log.v(TAG, "onPreviewFrame - data is null");
|
||||
}
|
||||
/*decodeYUV420SP(previewPixels, data, previewSize.width, previewSize.height);
|
||||
sendPicture(previewPixels);
|
||||
surfaceView.setVisibility(View.VISIBLE);
|
||||
camera.startPreview();
|
||||
surfaceView.setVisibility(View.INVISIBLE);*/
|
||||
}
|
||||
|
||||
|
||||
private Camera.Size getPreferredPreviewSize() {
|
||||
List<Camera.Size> previewSizes = parameters.getSupportedPreviewSizes();
|
||||
return previewSizes.get((int)Math.ceil(previewSizes.size() / 2));
|
||||
}
|
||||
|
||||
|
||||
private boolean prepareCapture(final Context context, int facing) {
|
||||
Log.v(TAG, "prepareCapture");
|
||||
if (!hasAllPermissions(context, PERMISSIONS)) {
|
||||
requestPermissions(context, PERMISSIONS);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (facing == 0) {
|
||||
camera = openBackCamera();
|
||||
}
|
||||
else {
|
||||
camera = openFrontCamera();
|
||||
}
|
||||
if (camera == null) {
|
||||
Log.v(TAG, "Unable to open camera");
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
parameters = camera.getParameters();
|
||||
for(Camera.Size size : parameters.getSupportedPictureSizes()) {
|
||||
Log.v(TAG, "Supported picture size: " + size.width + "x" + size.height);
|
||||
}
|
||||
for(Camera.Size size : parameters.getSupportedPreviewSizes()) {
|
||||
Log.v(TAG, "Supported preview size: " + size.width + "x" + size.height);
|
||||
}
|
||||
for(int format : parameters.getSupportedPictureFormats()) {
|
||||
Log.v(TAG, "Supported picture format: " + format);
|
||||
}
|
||||
for(int format : parameters.getSupportedPreviewFormats()) {
|
||||
Log.v(TAG, "Supported preview format: " + format);
|
||||
}
|
||||
Log.v(TAG, "Current preview format:" + parameters.getPreviewFormat());
|
||||
|
||||
pictureSize = parameters.getPictureSize();
|
||||
previewSize = getPreferredPreviewSize();
|
||||
parameters.setPreviewSize(previewSize.width, previewSize.height);
|
||||
Log.v(TAG, "Camera parameters. picture size: " + pictureSize.width + "x" + pictureSize.height + " preview size: " + previewSize.width + "x" + previewSize.height);
|
||||
//parameters.setPictureFormat(ImageFormat.RGB_565);
|
||||
//parameters.setPictureSize(previewSize.width, previewSize.height);
|
||||
camera.setParameters(parameters);
|
||||
|
||||
previewPixels = new byte[previewSize.width * previewSize.height * 3];
|
||||
picturePixels = new byte[pictureSize.width * pictureSize.height * 3];
|
||||
|
||||
//camera.setDisplayOrientation() // use utility function from Google example
|
||||
}
|
||||
catch(Exception e) {
|
||||
Log.e(TAG, e.toString());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private int getWidth() {
|
||||
return (previewSize != null) ? previewSize.width : 0;
|
||||
}
|
||||
|
||||
private int getHeight() {
|
||||
return (previewSize != null) ? previewSize.height : 0;
|
||||
}
|
||||
|
||||
private void startCapture(final Context context) {
|
||||
Log.v(TAG, "startCapture");
|
||||
((Activity)context).runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Log.v(TAG, "runOnUiThread");
|
||||
|
||||
try {
|
||||
Log.v(TAG, "Create surface");
|
||||
surfaceView = new SurfaceView(context);
|
||||
Activity activity = (Activity)context;
|
||||
ViewGroup viewGroup = (ViewGroup)activity.findViewById(android.R.id.content);
|
||||
viewGroup.addView(surfaceView);
|
||||
|
||||
final SurfaceHolder surfaceHolder = surfaceView.getHolder();
|
||||
surfaceHolder.addCallback(new SurfaceHolder.Callback() {
|
||||
@Override
|
||||
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
|
||||
Log.v(TAG, "surfaceChanged");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceCreated(SurfaceHolder holder) {
|
||||
Log.v(TAG, "surfaceCreated");
|
||||
try {
|
||||
camera.stopPreview();
|
||||
camera.setPreviewCallback(CameraExtension.this);
|
||||
camera.setPreviewDisplay(surfaceHolder);
|
||||
surfaceView.setVisibility(View.VISIBLE);
|
||||
camera.startPreview();
|
||||
surfaceView.setVisibility(View.INVISIBLE);
|
||||
/*Log.v(TAG, "Take picture");
|
||||
camera.takePicture(null, new Camera.PictureCallback() {
|
||||
@Override
|
||||
public void onPictureTaken(byte[] data, Camera camera) {
|
||||
if (data != null) {
|
||||
Log.v(TAG, "onPictureTaken");
|
||||
sendPicture(data);
|
||||
}
|
||||
else {
|
||||
Log.v(TAG, "onPictureTaken - data is null");
|
||||
}
|
||||
surfaceView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}, null);*/
|
||||
}
|
||||
catch(Exception e) {
|
||||
Log.e(TAG, e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceDestroyed(SurfaceHolder holder) {
|
||||
Log.v(TAG, "surfaceDestroyed");
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
catch(Exception e) {
|
||||
Log.e(TAG, e.toString());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void stopCapture() {
|
||||
if (camera != null) {
|
||||
camera.stopPreview();
|
||||
camera.release();
|
||||
camera = null;
|
||||
}
|
||||
if (surfaceView != null) {
|
||||
((ViewGroup)surfaceView.getParent()).removeView(surfaceView);
|
||||
surfaceView = null;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean PrepareCapture(final Context context, int facing) {
|
||||
Log.v(TAG, "PrepareCapture " + facing);
|
||||
return CameraExtension.getInstance().prepareCapture(context, facing);
|
||||
}
|
||||
|
||||
public static void StartCapture(final Context context) {
|
||||
Log.v(TAG, "StartCapture");
|
||||
CameraExtension.getInstance().startCapture(context);
|
||||
}
|
||||
|
||||
public static void StopCapture(final Context context) {
|
||||
Log.v(TAG, "StopCapture");
|
||||
CameraExtension.getInstance().stopCapture();
|
||||
}
|
||||
|
||||
public static int GetWidth() {
|
||||
Log.v(TAG, "GetWidth");
|
||||
return CameraExtension.getInstance().getWidth();
|
||||
}
|
||||
|
||||
public static int GetHeight() {
|
||||
Log.v(TAG, "GetHeight");
|
||||
return CameraExtension.getInstance().getHeight();
|
||||
}
|
||||
|
||||
}
|
@ -11,7 +11,7 @@
|
||||
#define DLIB_LOG_DOMAIN LIB_NAME
|
||||
#include <dmsdk/sdk.h>
|
||||
|
||||
#if defined(DM_PLATFORM_IOS) || defined(DM_PLATFORM_OSX)
|
||||
#if defined(DM_PLATFORM_IOS) || defined(DM_PLATFORM_OSX) || defined(DM_PLATFORM_ANDROID)
|
||||
|
||||
#include "camera_private.h"
|
||||
|
||||
@ -94,7 +94,7 @@ static int GetInfo(lua_State* L)
|
||||
static int GetFrame(lua_State* L)
|
||||
{
|
||||
DM_LUA_STACK_CHECK(L, 1);
|
||||
lua_rawgeti(L,LUA_REGISTRYINDEX, g_DefoldCamera.m_VideoBufferLuaRef);
|
||||
lua_rawgeti(L,LUA_REGISTRYINDEX, g_DefoldCamera.m_VideoBufferLuaRef);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
223
camera/src/camera_android.cpp
Normal file
223
camera/src/camera_android.cpp
Normal file
@ -0,0 +1,223 @@
|
||||
#include <dmsdk/sdk.h>
|
||||
|
||||
#if defined(DM_PLATFORM_ANDROID)
|
||||
|
||||
#include "camera_private.h"
|
||||
|
||||
static JNIEnv* Attach()
|
||||
{
|
||||
JNIEnv* env;
|
||||
JavaVM* vm = dmGraphics::GetNativeAndroidJavaVM();
|
||||
vm->AttachCurrentThread(&env, NULL);
|
||||
return env;
|
||||
}
|
||||
|
||||
static bool Detach(JNIEnv* env)
|
||||
{
|
||||
bool exception = (bool) env->ExceptionCheck();
|
||||
env->ExceptionClear();
|
||||
JavaVM* vm = dmGraphics::GetNativeAndroidJavaVM();
|
||||
vm->DetachCurrentThread();
|
||||
return !exception;
|
||||
}
|
||||
|
||||
namespace {
|
||||
struct AttachScope
|
||||
{
|
||||
JNIEnv* m_Env;
|
||||
AttachScope() : m_Env(Attach())
|
||||
{
|
||||
}
|
||||
~AttachScope()
|
||||
{
|
||||
Detach(m_Env);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static jclass GetClass(JNIEnv* env, const char* classname)
|
||||
{
|
||||
jclass activity_class = env->FindClass("android/app/NativeActivity");
|
||||
jmethodID get_class_loader = env->GetMethodID(activity_class,"getClassLoader", "()Ljava/lang/ClassLoader;");
|
||||
jobject cls = env->CallObjectMethod(dmGraphics::GetNativeAndroidActivity(), get_class_loader);
|
||||
jclass class_loader = env->FindClass("java/lang/ClassLoader");
|
||||
jmethodID find_class = env->GetMethodID(class_loader, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;");
|
||||
|
||||
jstring str_class_name = env->NewStringUTF(classname);
|
||||
jclass outcls = (jclass)env->CallObjectMethod(cls, find_class, str_class_name);
|
||||
env->DeleteLocalRef(str_class_name);
|
||||
return outcls;
|
||||
}
|
||||
|
||||
|
||||
struct AndroidCamera {
|
||||
dmBuffer::HBuffer m_VideoBuffer;
|
||||
|
||||
AndroidCamera() : m_VideoBuffer(0)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
AndroidCamera g_Camera;
|
||||
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
JNIEXPORT void JNICALL Java_com_defold_android_camera_CameraExtension_helloworld(JNIEnv *env, jobject obj) {
|
||||
dmLogError("Hello World!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_com_defold_android_camera_CameraExtension_sendPicture(JNIEnv *env, jobject obj, jbyteArray array) {
|
||||
dmLogError("send Picture!\n");
|
||||
|
||||
uint8_t* data = 0;
|
||||
uint32_t datasize = 0;
|
||||
dmBuffer::GetBytes(g_Camera.m_VideoBuffer, (void**)&data, &datasize);
|
||||
|
||||
int len = env->GetArrayLength(array);
|
||||
dmLogError("sendPicture received array length %d expected %d", len, datasize);
|
||||
//unsigned char* buf = new unsigned char[len];
|
||||
env->GetByteArrayRegion (array, 0, len, reinterpret_cast<jbyte*>(data));
|
||||
|
||||
dmBuffer::ValidateBuffer(g_Camera.m_VideoBuffer);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
JNIEXPORT void JNICALL Java_com_defold_android_camera_CameraExtension_handleCameraFrame(JNIEnv *env, jobject obj, jbyteArray yuv420sp, jint width, jint height) {
|
||||
uint8_t* data = 0;
|
||||
uint32_t datasize = 0;
|
||||
dmBuffer::GetBytes(g_Camera.m_VideoBuffer, (void**)&data, &datasize);
|
||||
|
||||
int i;
|
||||
int j;
|
||||
int Y;
|
||||
int Cr = 0;
|
||||
int Cb = 0;
|
||||
int pixPtr = 0;
|
||||
int jDiv2 = 0;
|
||||
int R = 0;
|
||||
int G = 0;
|
||||
int B = 0;
|
||||
int cOff;
|
||||
int w = width;
|
||||
int h = height;
|
||||
int sz = w * h;
|
||||
|
||||
jbyte* yuv = (jbyte*) (env->GetPrimitiveArrayCritical(yuv420sp, 0));
|
||||
|
||||
for(j = 0; j < h; j++) {
|
||||
pixPtr = j * w;
|
||||
jDiv2 = j >> 1;
|
||||
for(i = 0; i < w; i++) {
|
||||
Y = yuv[pixPtr];
|
||||
if(Y < 0) Y += 255;
|
||||
if((i & 0x1) != 1) {
|
||||
cOff = sz + jDiv2 * w + (i >> 1) * 2;
|
||||
Cb = yuv[cOff];
|
||||
if(Cb < 0) Cb += 127; else Cb -= 128;
|
||||
Cr = yuv[cOff + 1];
|
||||
if(Cr < 0) Cr += 127; else Cr -= 128;
|
||||
}
|
||||
|
||||
//ITU-R BT.601 conversion
|
||||
//
|
||||
//R = 1.164*(Y-16) + 2.018*(Cr-128);
|
||||
//G = 1.164*(Y-16) - 0.813*(Cb-128) - 0.391*(Cr-128);
|
||||
//B = 1.164*(Y-16) + 1.596*(Cb-128);
|
||||
//
|
||||
Y = Y + (Y >> 3) + (Y >> 5) + (Y >> 7);
|
||||
R = Y + (Cr << 1) + (Cr >> 6);
|
||||
if(R < 0) R = 0; else if(R > 255) R = 255;
|
||||
G = Y - Cb + (Cb >> 3) + (Cb >> 4) - (Cr >> 1) + (Cr >> 3);
|
||||
if(G < 0) G = 0; else if(G > 255) G = 255;
|
||||
B = Y + Cb + (Cb >> 1) + (Cb >> 4) + (Cb >> 5);
|
||||
if(B < 0) B = 0; else if(B > 255) B = 255;
|
||||
|
||||
data[0] = R;
|
||||
data++;
|
||||
data[0] = G;
|
||||
data++;
|
||||
data[0] = B;
|
||||
data++;
|
||||
//data[(pixPtr * 3) + 0] = R;
|
||||
//data[(pixPtr * 3) + 1] = G;
|
||||
//data[(pixPtr * 3) + 2] = B;
|
||||
//data[pixPtr++] = 0xff000000 + (R << 16) + (G << 8) + B;
|
||||
}
|
||||
}
|
||||
|
||||
env->ReleasePrimitiveArrayCritical(yuv420sp, yuv, 0);
|
||||
|
||||
dmBuffer::ValidateBuffer(g_Camera.m_VideoBuffer);
|
||||
}
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
int CameraPlatform_StartCapture(dmBuffer::HBuffer* buffer, CameraType type, CaptureQuality quality, CameraInfo& outparams)
|
||||
{
|
||||
dmLogError("Android start capture");
|
||||
|
||||
int facing = (type == CAMERA_TYPE_BACK) ? 0 : 1;
|
||||
|
||||
// prepare JNI
|
||||
AttachScope attachscope;
|
||||
JNIEnv* env = attachscope.m_Env;
|
||||
jclass cls = GetClass(env, "com.defold.android.camera.CameraExtension");
|
||||
|
||||
// prepare camera for capture and get width and height
|
||||
jmethodID prepare_capture = env->GetStaticMethodID(cls, "PrepareCapture", "(Landroid/content/Context;I)Z");
|
||||
jboolean prepare_success = env->CallStaticBooleanMethod(cls, prepare_capture, dmGraphics::GetNativeAndroidActivity(), facing);
|
||||
jint width = env->CallStaticIntMethod(cls, env->GetStaticMethodID(cls, "GetWidth", "()I"));
|
||||
jint height = env->CallStaticIntMethod(cls, env->GetStaticMethodID(cls, "GetHeight", "()I"));
|
||||
|
||||
// set out parameters and create video buffer
|
||||
outparams.m_Width = (uint32_t)width;
|
||||
outparams.m_Height = (uint32_t)height;
|
||||
uint32_t size = outparams.m_Width * outparams.m_Height;
|
||||
dmBuffer::StreamDeclaration streams_decl[] = {
|
||||
{dmHashString64("rgb"), dmBuffer::VALUE_TYPE_UINT8, 3}
|
||||
};
|
||||
dmBuffer::Create(size, streams_decl, 1, buffer);
|
||||
g_Camera.m_VideoBuffer = *buffer;
|
||||
|
||||
// Start capture
|
||||
if (prepare_success == JNI_TRUE) {
|
||||
jmethodID start_capture = env->GetStaticMethodID(cls, "StartCapture", "(Landroid/content/Context;)V");
|
||||
env->CallStaticBooleanMethod(cls, start_capture, dmGraphics::GetNativeAndroidActivity());
|
||||
}
|
||||
|
||||
return (prepare_success == JNI_TRUE) ? 1 : 0;
|
||||
}
|
||||
|
||||
int CameraPlatform_StopCapture()
|
||||
{
|
||||
dmLogError("Android stop capture");
|
||||
|
||||
// stop Android camera
|
||||
AttachScope attachscope;
|
||||
JNIEnv* env = attachscope.m_Env;
|
||||
jclass cls = GetClass(env, "com.defold.android.camera.CameraExtension");
|
||||
jmethodID method = env->GetStaticMethodID(cls, "StopCapture", "(Landroid/content/Context;)V");
|
||||
dmLogError("Android stop capture - calling java");
|
||||
env->CallStaticVoidMethod(cls, method, dmGraphics::GetNativeAndroidActivity());
|
||||
|
||||
// destroy the video buffer
|
||||
dmBuffer::Destroy(g_Camera.m_VideoBuffer);
|
||||
g_Camera.m_VideoBuffer = 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
#endif // DM_PLATFORM_ANDROID
|
@ -19,7 +19,7 @@ struct CameraInfo
|
||||
{
|
||||
uint32_t m_Width;
|
||||
uint32_t m_Height;
|
||||
CameraType m_Type;
|
||||
CameraType m_Type;
|
||||
};
|
||||
|
||||
extern int CameraPlatform_StartCapture(dmBuffer::HBuffer* buffer, CameraType type, CaptureQuality quality, CameraInfo& outparams);
|
||||
|
@ -23,6 +23,7 @@ bundle_identifier = com.defold.camera
|
||||
|
||||
[android]
|
||||
package = com.defold.camera
|
||||
manifest = /AndroidManifest.xml
|
||||
|
||||
[osx]
|
||||
bundle_identifier = com.defold.camera
|
||||
|
@ -1,3 +1,6 @@
|
||||
images {
|
||||
image: "/main/images/logo.png"
|
||||
}
|
||||
margin: 0
|
||||
extrude_borders: 0
|
||||
inner_padding: 0
|
||||
|
Loading…
x
Reference in New Issue
Block a user