Calling Android Camera from a C++ code causes SIGSEGV

I don’t know how to address this properly. But I will briefly discuss what I am trying to do.

We all know that there is no direct way of accessing the camera directly from Cocos2d-x. The only way to do this is to use JNI. Let the camera be called through Android code, save the image to disk and let the Cocos2dx game access that image for use.

That is exactly what I am doing with my code. I saw this code somewhere online and try to do the same thing with my code.

The only difference is that I am accessing the method whose signature is just void(). Since I am going to call the camera using this trivial code:

public static void callPhoneCamera()
{
    Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    
    if (takePictureIntent.resolveActivity(mSelf.getPackageManager()) != null) 
        mSelf.startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
}

Here is my JNI related code:

JniLink.cpp

#include "../../cocos2d/cocos/platform/android/jni/JniHelper.h"
#include "../proj.android/jni/JniLink.h"

extern "C"
{
	void callPhoneCamera()
	{
		cocos2d::JniMethodInfo t;

		if(cocos2d::JniHelper::getStaticMethodInfo(
				t
				, "com/indigo/android/mysamplecamera/AppActivity"
				, "callPhoneCamera"
				, "()V"))
		{
			t.env->CallStaticVoidMethod(t.classID , t.methodID);
		}
	}
}

JniLink.hpp

#ifndef JNILINK_H_
#define JNILINK_H_

extern "C"
{
	extern void callPhoneCamera();
}

#endif

This compiles fine until during run time it ANRs on the emulator for Android 5 and on my phone Android 4.

Also I really don’t understand the semantics of paramCode. On the link I provided it was written this way:

(Ljava/lang/String;)V

I understand the L being the type. I don’t understand V. I assume that it is the return type which is void, thus ‘V’. With this in mind, I wrote it as “()V” since the function doesn’t have parameter at all and the return type is void.

Can you please help me on how to go about this? I haven’t got the chance to learn JNI and I still have a long way to go.

How can I call ‘callPhoneCamera’, a Java function, into a cpp code?

Thank you!

You should try calling that java function from the ui thread. Something like that :

public static void callPhoneCamera()
{
	activity.runOnUiThread(new Runnable()
	{	
		@Override
		public void run()
		{
			Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

			if (takePictureIntent.resolveActivity(mSelf.getPackageManager()) != null) 
				mSelf.startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
		}
	});
}

@TheCodez

Thank you for the heads up! Yes it works. Also I would like to add, one must provide the fully qualified name of the class where the method is declared:

void HelloWorld::takePhoto()
{
	#if(CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
	
	JniMethodInfo t;

		if (JniHelper::getStaticMethodInfo(t
			, "com/indigo/android/cocoscamera/AppActivity"
			, "accessPhoneCamera"
			, "()V"));
		{
			t.env->CallStaticVoidMethod(t.classID, t.methodID);
			t.env->DeleteLocalRef(t.classID);
		}

		CCLOG("HelloWorldScene::takePhoto(): jni method called.");

	#endif

	CCLOG("HelloWorldScene::takePhoto() called.");
}

Take note of the line where: "com/indigo/android/cocoscamera/AppActivity". Before I only set this as the class name. If you didn’t it also reproduces the same SIGSEGV fault. By applying the fully qualified name I was able to call the class.

Thank you very much!

For documentation purposes:

This link provided me a better information on JNI:

AppActivity.java (This is the main activity class in proj.android/src// folder. Just add the following lines to look like this:

package com.indigo.android.cocoscamera;

import org.cocos2dx.lib.Cocos2dxActivity;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.provider.MediaStore;

public class AppActivity extends Cocos2dxActivity 
{
	private static int REQUEST_IMAGE_CAPTURE = 1;
	private static Activity mActivity = null;
	
	@Override
	public void onCreate(Bundle savedInstaceState)
	{
		super.onCreate(savedInstaceState);
		mActivity = this;
	}
	
	public static void accessPhoneCamera()
	{
		if(mActivity == null)
			return;
		
		mActivity.runOnUiThread(new Runnable()
		{
			@Override
			public void run()
			{
			    Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
			    
			    if (takePictureIntent.resolveActivity(mActivity.getPackageManager()) != null) 
			    	mActivity.startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
			}
		});
	}
}

On C++ code do this:

#if(CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
	#include "../cocos2d/cocos/platform/android/jni/JniHelper.h"
#endif

#include "HelloWorldScene.h"

USING_NS_CC;

Scene* HelloWorld::createScene()
{
    auto scene = Scene::create();
    auto layer = HelloWorld::create();

    scene->addChild(layer);

    return scene;
}

bool HelloWorld::init()
{
    if ( !Layer::init() )
    {
        return false;
    }
    
    Size visibleSize = Director::getInstance()->getVisibleSize();
    Vec2 origin = Director::getInstance()->getVisibleOrigin();

	cocos2d::Vec2 centerPos = cocos2d::Vec2(visibleSize.width / 2.0f, visibleSize.height / 2.0f);
		
	mCameraButton = cocos2d::ui::Button::create("asset_large_btn_up.png", "asset_large_btn_down.png", "");
	mCameraButton->setAnchorPoint(cocos2d::Vec2(0.5f, 0.5f));
	mCameraButton->setPosition(centerPos);
	mCameraButton->setTitleText("Take a Photo");
	mCameraButton->setTitleFontSize(24.0f);
	mCameraButton->setTitleColor(cocos2d::Color3B::YELLOW);
	mCameraButton->addTouchEventListener
		(
			[&](cocos2d::Ref * sender, cocos2d::ui::Widget::TouchEventType type) 
			{
				if (type == cocos2d::ui::Widget::TouchEventType::BEGAN)
				{
					takePhoto();
				}
			}
		);

	addChild(mCameraButton, 0);

    return true;
}

void HelloWorld::menuCloseCallback(Ref* pSender)
{
    Director::getInstance()->end();

	#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
		exit(0);
	#endif
}

void HelloWorld::takePhoto()
{
	#if(CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
	
	JniMethodInfo t;

		if (JniHelper::getStaticMethodInfo(t
			, "com/indigo/android/cocoscamera/AppActivity"
			, "accessPhoneCamera"
			, "()V"));
		{
			t.env->CallStaticVoidMethod(t.classID, t.methodID);
			t.env->DeleteLocalRef(t.classID);
		}

		CCLOG("HelloWorldScene::takePhoto(): jni method called.");

	#endif

	CCLOG("HelloWorldScene::takePhoto() called.");
}

I am having problem compiling this on Visual Studio Community 2015. <jni.h> errors out as no such file directory. So if you are planning a cross-platform with this code, you are on your own as I am still figuring it out as of the moment.

Thank you! I hope this helps someone.

2 Likes