Native Camera for Android & iOS [Open Source]

Hello there,

NativeCamera is a native Unity plugin that helps you take pictures/record videos with your device’s camera on Android and iOS. It has built-in support for runtime permissions, as well.

Asset Store: Native Camera for Android & iOS | Integration | Unity Asset Store
Also available at: GitHub - yasirkula/UnityNativeCamera: A native Unity plugin to take pictures/record videos with device camera on Android & iOS
Discord: yasirkula Unity Assets
GitHub Sponsors :coffee:

FAQ

  • Audio is muted on iOS after calling NativeCamera.RecordVideo

Please see: Native Camera for Android & iOS [Open Source] page-9#post-8207157

  • Can’t use the camera, it says “java.lang.ClassNotFoundException: com.yasirkula.unity.NativeCamera” in Logcat

If you are sure that your plugin is up-to-date, then enable Custom Proguard File option from Player Settings and add the following line to that file: -keep class com.yasirkula.unity.* { *; }

Enjoy!

4 Likes

Now available on Asset Store!

2 Likes

Hello,

Not able to get this to work.

I’ve done:

  1. set Write Permission to External (SDCard) in Player Settings
    2. Changed my manifest according to recomendations:
<?xml version="1.0" encoding="utf-8"?>
<manifest
    xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.unity3d.player"
    xmlns:tools="http://schemas.android.com/tools"
    android:installLocation="preferExternal"
    android:versionCode="1"
    android:versionName="1.0">
    <uses-feature android:name="android.hardware.camera" android:required="true" />
    <supports-screens
        android:smallScreens="true"
        android:normalScreens="true"
        android:largeScreens="true"
        android:xlargeScreens="true"
        android:anyDensity="true"/>

    <application
        android:theme="@style/UnityThemeSelector"
        android:icon="@drawable/app_icon"
        android:label="@string/app_name"
        android:debuggable="true">
        <activity android:name="com.unity3d.player.UnityPlayerActivity"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            <meta-data android:name="unityplayer.UnityActivity" android:value="true" />
        </activity>
        <provider
              android:name="com.furiogames.unity.NativeCameraContentProvider"
              android:authorities="furiogames"
              android:exported="false"
              android:grantUriPermissions="true"
         </provider>
    </application>
</manifest>
  1. created a gameobject with this script attached:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class PicFromCamera : MonoBehaviour {


    public Text textUIValue;

    Vector3 currentPositon = new Vector3();

    private int updateCount;

    void Update()
    {
        updateCount++;
        textUIValue.text = "Update Count: " + updateCount.ToString();
        if( Input.GetMouseButtonDown( 0 ) )
        {
            textUIValue.text = "Registered MouseDown";
            // Don't attempt to use the camera if it is already open
            if( NativeCamera.IsCameraBusy() )
                return;

            if( Input.mousePosition.x < Screen.width / 2 )
            {
                // Take a picture with the camera
                // If the captured image's width and/or height is greater than 512px, down-scale it
                TakePicture( 512 );
            }
            else
            {
                // Record a video with the camera
                RecordVideo();
            }
        }

        if (Input.touchCount > 0) {
            textUIValue.text = "TouchCount > 0";
            if( NativeCamera.IsCameraBusy() )
                return;
        
            Touch touch = Input.GetTouch (0); // get first touch since touch count is greater than zero
            currentPositon = Camera.main.ScreenToWorldPoint (new Vector3 (touch.position.x, touch.position.y, 10));


            if (touch.phase == TouchPhase.Began) {
                textUIValue.text = "Touch Began";


                if(currentPositon.x < Screen.width / 2 )
                {
                    // Take a picture with the camera
                    // If the captured image's width and/or height is greater than 512px, down-scale it
                    TakePicture( 512 );
                }
                else
                {
                    // Record a video with the camera
                    RecordVideo();
                }
            }

            if (touch.phase == TouchPhase.Ended || touch.phase == TouchPhase.Canceled) {
            }

            if (touch.phase == TouchPhase.Moved) {
                textUIValue.text = "Registered Touch Began";
                if(currentPositon.x < Screen.width / 2 )
                {
                    // Take a picture with the camera
                    // If the captured image's width and/or height is greater than 512px, down-scale it
                    TakePicture( 512 );
                }
                else
                {
                    // Record a video with the camera
                    RecordVideo();
                }

            }
        }

    }

    private void TakePicture( int maxSize )
    {
        NativeCamera.Permission permission = NativeCamera.TakePicture( ( path ) =>
            {
                textUIValue.text = "Image path: " + path;

                Debug.Log( "Image path: " + path );
                if( path != null )
                {
                    // Create a Texture2D from the captured image
                    Texture2D texture = NativeCamera.LoadImageAtPath( path, maxSize );
                    if( texture == null )
                    {
                        Debug.Log( "Couldn't load texture from " + path );
                        return;
                    }

                    // Assign texture to a temporary quad and destroy it after 5 seconds
                    GameObject quad = GameObject.CreatePrimitive( PrimitiveType.Quad );
                    quad.transform.position = Camera.main.transform.position + Camera.main.transform.forward * 2.5f;
                    quad.transform.forward = Camera.main.transform.forward;
                    quad.transform.localScale = new Vector3( 1f, texture.height / (float) texture.width, 1f );

                    Material material = quad.GetComponent<Renderer>().material;
                    if( !material.shader.isSupported ) // happens when Standard shader is not included in the build
                        material.shader = Shader.Find( "Legacy Shaders/Diffuse" );

                    material.mainTexture = texture;

                    Destroy( quad, 5f );

                    // If a procedural texture is not destroyed manually,
                    // it will only be freed after a scene change
                    Destroy( texture, 5f );
                }
            }, maxSize );

        Debug.Log( "Permission result: " + permission );
    }

    private void RecordVideo()
    {
        NativeCamera.Permission permission = NativeCamera.RecordVideo( ( path ) =>
            {
                textUIValue.text = "Video path: " + path;

                Debug.Log( "Video path: " + path );
                if( path != null )
                {
                    // Play the recorded video
                    Handheld.PlayFullScreenMovie( "file://" + path );
                }
            } );

        Debug.Log( "Permission result: " + permission );
    }
}

I see various debugs in editor correctly (can’t access camera of course which is understandable) but on mobile the update counter just stops and nothing else when I touch screen. I assume the camera is trying to be accessed? I get a dialog asking permission and of course I grant access when app starts up.

Using Samsung Galaxy S8, Unity 2017.1.0f3, mono 5.9.6

Sorry for the post length but I just wanted to be thorough.

Any ideas?

Your Gallery asset works perfectly on device so not sure why this would not?

Sample project is here:

Thanks!

Joseph

The only fatal issue I could spot was this: android:name="com.furiogames.unity.NativeCameraContentProvider"

You shouldn’t change the value of android:name (leave it as com.yasirkula.unity.NativeCameraContentProvider) because it specifies the path of the content provider class. Please let me know if that works.

Thanks for the response!

I did have it as your name originally and still no go. Decided to change to mine just to see and forgot to change it back. Just changed it back to yours and still no go.

Very strange it isn’t working. Does the project I sent work for you? If so then this may be an issue with my setup in some way.

The crucial part here is to move AndroidManifest.xml from Plugins/NativeCamera/Android to Plugins/Android. It is a must. Otherwise, Unity just ignores the manifest.

I also had to change </provider> to /> and tweak Update like this:

void Update()
{
    updateCount++;
    textUIValue.text = "Update Count: " + updateCount.ToString();
    if( Input.GetMouseButtonDown( 0 ) )
    {
        textUIValue.text = "Registered MouseDown";
        // Don't attempt to use the camera if it is already open
        if( NativeCamera.IsCameraBusy() )
            return;

        if( Input.mousePosition.x < Screen.width / 2 )
        {
            // Take a picture with the camera
            // If the captured image's width and/or height is greater than 512px, down-scale it
            TakePicture( 512 );
        }
        else
        {
            // Record a video with the camera
            RecordVideo();
        }
    }
       
    if (Input.touchCount > 0) {
        //    Ray ray = Camera.main.ScreenPointToRay(Input.GetTouch(i).position);
        textUIValue.text = "Registered TouchCount > 0";
    }
}
2 Likes

Wow! Thank you for taking your time to help with this!

I updated to Unity 2018.1.0 made your changes, updated Android SDK (to get API level 25) and it mostly works now.

Having a small issue with if I take a photo it isn’t returning to displaying on plane. It stays at camera. If I take a second pic then it switches back to display on plane fine. It does this with vid also. I’ll play with it some more.

Thanks again!

UPDATE: Just switched take pic/vid from screen touch to buttons and now there is no issue.

2 Likes

That’s great news! Thanks for the info.

Hi, how do you grab the camera’s pixels and save them to a texture like WebCamTexture but not directly to a file? I need to preview them like any camera app does.

Thanks.

NativeCamera doesn’t support live preview inside Unity. Why don’t you use WebCamTexture for this?

Hi, First, thanks a lot for making this plugin freely available. It has been really really helpful.

I have been using this plugin successfully in iOS and android. As per our current requirement, we need as lowest resolution as possible for video recording.
We tested on various android devices and found the lowest is 640X480. Just wanted to know if there is any way we can manually specify the resolution to go below it.

Unfortunately not. AFAIK, resolution only depends on EXTRA_VIDEO_QUALITY on Android and UIImagePickerController.videoQuality on iOS, and they don’t accept custom resolutions. If you really need a lower resolution, you’ll probably have to use another plugin that renders the camera output directly inside Unity.

Hi, I’m getting a “Can’t find ContentProvider, camera is inaccessible!”

I didn’t see anyone else post it, so trying my luck here.

I added the provider to the manifest. Here’s the code, rather simple:

            NativeCamera.Permission permission = NativeCamera.TakePicture((path) =>
            {
                Debug.Log(path);
            });

It does prompt for the needed permissions, but then Unity freezes after that.

By the way, huge fan. Thanks for all the assets you have released. Probably the best asset provider I know of, up there along with the usefulness of Catlike Coding. Keep being awesome.

2 Likes

After building your project to Android, can you check the contents of PROJECT_PATH/Temp/StagingArea/AndroidManifest.xml and verify that the provider exists? If it does exist, can I see it?

Sure. It’s the same though, except as 1 line.

<provider android:name="com.yasirkula.unity.NativeCameraContentProvider" android:authorities="3DBear_Camera_Authority" android:exported="false" android:grantUriPermissions="true" />

Hmm, can you try this android:authorities: “bearcameraauth”

Hey, sorry for wasting your time! It was a pretty obvious error on my side, I had put the snippet into an ‘activity’ that was under ‘application’. Gotta hate the XML format… thanks for helping!

1 Like

Nice job- Thank you for creating this open source plugin!

Hey bros really amazing pluggin, all ok but, how can i make the image more big and more light? its little and dark. thanks

Is NativeCamera image smaller than an image captured via native camera app? What are their resolutions? Are there no quality settings in the camera overlay in NativeCamera?

To decrease the darkness, you should play with the exposure settings in the camera overlay. Please note that NativeCamera simply starts the native camera app and fetches the captured image from it, the rest is up to the camera app itself.