Webcam on Android not working?

I’ve got live camera input working both on the desktop (MacBook Pro) and iOS (iPhone4), but I can’t get anything out of either of my Android phones (LG Optimus and Samsung Galaxy Nexus).

Here’s my script (attach it to a plane with a RenderTexture)

#pragma strict
private var devices : WebCamDevice[];
public var deviceName : String;
private var wct : WebCamTexture;
private var resultString : String;


function Start() {
    yield Application.RequestUserAuthorization (UserAuthorization.WebCam | UserAuthorization.Microphone);

    if (Application.HasUserAuthorization(UserAuthorization.WebCam | UserAuthorization.Microphone)) {
       devices = WebCamTexture.devices;
       deviceName = devices[0].name;
       wct = new WebCamTexture(deviceName, 640, 480, 30);
       renderer.material.mainTexture = wct;
       wct.Play();
       resultString = "no problems";
    } else {
       resultString = "no permission!";
    }
}

function OnGUI() {
    for (var i = 0; i < devices.length; i++) {
       GUI.Box(Rect(100, 100+(i*25), 300, 25),devices[i].name);
    }
    GUI.Box(Rect(100, 100+(i*25), 400, 25),resultString);
}

Would appreciate any advice or pointers.

(also posted on Unity Answers http://answers.unity3d.com/questions/217940/)

maybe you should do some setting in android manifest file. To require some authorization eh…

So, is there a way to make it working on android?

BUMP.

Come on. Someone must have some ideas or advice. Or is no one working with cameras on Android?

Here’s my latest test script, and a jpg of the results.

#pragma strict
private var devices : WebCamDevice[];
private var deviceName : String;
private var wct : WebCamTexture;
private var resultString : String;
private var update: boolean;
private var data : Color32[];

function Start() {
    yield Application.RequestUserAuthorization (UserAuthorization.WebCam | UserAuthorization.Microphone);
    if (Application.HasUserAuthorization(UserAuthorization.WebCam | UserAuthorization.Microphone)) {
       devices = WebCamTexture.devices;
       deviceName = devices[0].name;
       wct = new WebCamTexture(deviceName, 640, 360, 15);
       renderer.material.mainTexture = wct;
       wct.Play();
       resultString = "no problems";
    } else {
       resultString = "no permission!";
    }
    data = new Color32[wct.width * wct.height];
}

function Update() {
	if (wct) {
		if (wct.didUpdateThisFrame) {
			update = true;
	        wct.GetPixels32 (data);
		} else {
			update = false;
		}
	}
}
 
function OnGUI() {
    for (var i = 0; i < devices.length; i++) {
       GUI.Box(Rect(100, 100+(i*25), 200, 25),"NAME: "+devices[i].name);
       GUI.Box(Rect(300, 100+(i*25), 200, 25),"FRONT FACING? "+devices[i].isFrontFacing);
    }
    GUI.Box(Rect(100, 100+(i*25), 200, 25),"OPENED? "+resultString);
    GUI.Box(Rect(300, 100+(i*25), 200, 25),"PLAYING? "+wct.isPlaying);
    GUI.Box(Rect(100, 125+(i*25), 200, 25),"UPDATED? "+update);
}

855058--31894--$IMG_1607.jpg

You completely ignored the only person who gave you advice, and it was good advice. Until you answer them, why would anyone suggest anything else?

Did you update the manifest?

OK, fair enough.

I did look at the manifest and couldn’t find anything wrong, but maybe I’m missing something.

Here it is:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="preferExternal" package="edu.usc.perrywct" android:versionName="1.0" android:versionCode="1">
  <supports-screens android:smallScreens="true" android:normalScreens="true" android:largeScreens="true" android:xlargeScreens="true" android:anyDensity="true" />
  <application android:icon="@drawable/app_icon" android:label="@string/app_name" android:debuggable="false">
    <activity android:name="com.unity3d.player.UnityPlayerProxyActivity" android:label="@string/app_name" android:configChanges="fontScale|keyboard|keyboardHidden|locale|mnc|mcc|navigation|orientation|screenLayout|screenSize|smallestScreenSize|uiMode|touchscreen" android:screenOrientation="reverseLandscape">
      <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
      </intent-filter>
    </activity>
    <activity android:name="com.unity3d.player.UnityPlayerActivity" android:label="@string/app_name" android:configChanges="fontScale|keyboard|keyboardHidden|locale|mnc|mcc|navigation|orientation|screenLayout|screenSize|smallestScreenSize|uiMode|touchscreen" android:screenOrientation="reverseLandscape">
    </activity>
    <activity android:name="com.unity3d.player.UnityPlayerNativeActivity" android:label="@string/app_name" android:configChanges="fontScale|keyboard|keyboardHidden|locale|mnc|mcc|navigation|orientation|screenLayout|screenSize|smallestScreenSize|uiMode|touchscreen" android:screenOrientation="reverseLandscape">
      <meta-data android:name="android.app.lib_name" android:value="unity" />
      <meta-data android:name="unityplayer.ForwardNativeEventsToDalvik" android:value="false" />
    </activity>
    <activity android:name="com.unity3d.player.VideoPlayer" android:label="@string/app_name" android:configChanges="fontScale|keyboard|keyboardHidden|locale|mnc|mcc|navigation|orientation|screenLayout|screenSize|smallestScreenSize|uiMode|touchscreen" android:screenOrientation="reverseLandscape">
    </activity>
  </application>
  <uses-feature android:glEsVersion="0x00020000" />
  <uses-permission android:name="android.permission.INTERNET" />
  <uses-permission android:name="android.permission.WAKE_LOCK" />
  <uses-permission android:name="android.permission.CAMERA" />
  <uses-feature android:name="android.hardware.camera" android:required="true" />
  <uses-feature android:name="android.hardware.camera.front" android:required="true" />
  <uses-sdk android:minSdkVersion="6" android:targetSdkVersion="15" />
</manifest>

Have you tried using default values for width, height and refresh rate? Maybe the settings you’re requesting aren’t possible with the device.

Unfortunately, I don’t know how to query the phone to figure out what the default values are - specs on the phone are 1080p on the main camera, 720p on the front-facing one - I’ve tried those and every other standard resolution I can think of, but none of them make any difference

In any case, according to the script reference, all I’m specifying are the requested width, height, fps:

“The real width [height, frame rate] may be different, and will match the closest width [height, frame rate] supported by the device.”

So shouldn’t it work whatever resolution I’m requesting?

Meanwhile, does anyone have any suggestions regarding the AndroidManifest file? Does anything look wrong? I’d really love to get this working.

Well what I meant was not specifying a width/height/refresh rate. You can call the constructor without those things.

Have you tried just doing a really stripped down test version? I can get the camera to work absolutely fine with:

Start:

m_CameraTexture = new WebCamTexture(WebCamTexture.devices[0].name);

if (m_CameraTexture != null)
{
      m_CameraTexture.Play();
}

Update:

if (m_CameraTexture != null  m_CameraTexture.didUpdateThisFrame)
{
       transform.renderer.material.mainTexture = m_CameraTexture;
}

Didn’t realize I could call constructor without those parameters, thanks.

Tried it. Still nothing. Just like the more complicated version: when I run your stripped down version on the desktop or iPhone, it works fine.

On Android (Samsung Galaxy Nexus), just a black plane.

I don’t have anything productive to offer, other than a “me too” - on an Asus Eee Transformer TF-101 (last year’s version), it reports two cameras and everything looks fine except the picture is just black, whichever camera I use. I tried the same authorization request code that you’re using, and confirmed via OnGUI that HasUserAuthorization is returning true.

I have no idea where to find the manifest file, let alone what it ought to look like! As far as I can tell, Unity generates an APK and copies it straight to the device, and everything works except the camera.

I found a lot of old threads, and UnityAnswers questions, on this topic but I think MADmarine’s post is the first time I’ve seen anybody saying it actually works for them!

I guess I should count myself lucky then…

If it helps I’m running Android 3.1 on a Toshiba AT100 Tablet. Have you checked if there are any Android updates to your devices?

George - The Android Manifest file is created when you build your application for Android - there’s a Temp folder in the project folder, inside that a StagingArea folder gets created when you build, and inside that is the AndroidManifest.xml file. The manifest gets created on the fly every time you build your app, but as I understand it, you can move it to Assets/Plugins/Android/, make any changes you want to it, and then that’s the manifest that will be loaded next time you build. Someone with more knowledge than me should confirm that I’ve got all the facts right on this.

And (speaking of people with more knowledge than me) I would really appreciate it if someone - anyone - who understands these things could take a look at the Android Manifest file that I posted above and make some suggestions about what alterations or updates might have an effect.

MADmarine - phones are up to date, don’t think that’s the problem

tonydongyiqi? wccrawford? Anyone? Care to help us out here?

Someone just PM’ed me with a few more questions about how I got the camera to work on my tablet, I thought I’d just share some of my answers just in case it helps anyone else:

I think it may be written somewhere that Android 4.0.3 is needed for the camera, but it is definitely not required for it to run on the tablet/phone. I do think you have to have the 4.0.3 SDK installed on your development system however. I’m not sure what the key bit of info is to getting the camera to work, so I’ll just list as many things as I can about what I’m doing and hopefully you’ll manage to get it working.

I made sure that I downloaded the latest SDK for Android along with some others and pointed Unity at that. So have on my system the SDKs for 4.0.3, 2.3.3, and 2.1 installed. And that’s WITH the Google APIs for each.

In my Unity settings I put the minimum API Level as 2.3.3 Gingerbread (API level 10).

The other settings are mostly at default (ARMv7 only, OpenGL ES 2.0, with auto install and internet access, write access to external storage).

I don’t know if this also makes a massive difference, but I don’t start up my camera straight away. I find that if I try to do too much with the camera in a single frame my tablet crashes too. So perhaps try having a button in your app that will start up the camera (just for testing this out anyway), don’t try to start up at the same time as the app.

I’ve got all the Android SDKs installed, and the Google APIs as well.

As far as “pointing Unity towards the SDK” - you just mean the setting in Preferences/External Tools/Android SDK Location, right? I have that set to /Developer/android-sdk-macosx

I tried changing the minimum API level at 2.3.3 (and tried 4.0 also, since that’s what’s running on the phone).

I tried using a button to start the camera up, instead of starting it up with the app.

Still getting nothing but a big black rectangle.

Anyway, MADmarine, I really appreciate all your help on this, whether we’ve solved it or not.

Obviously I’m not the only one having this problem, but in the absence of anyone else chiming in with any ideas or suggestions (by now I would have expected somebody from Unity to offer some kind of support) I guess I will have to conclude that this is a bug, and that I should just file a bug report and give up until it gets fixed.

#pragma strict

private var m_CameraTexture : WebCamTexture;

function Start () {
}

function Update () {
	if (m_CameraTexture != null  m_CameraTexture.didUpdateThisFrame) {
	       transform.renderer.material.mainTexture = m_CameraTexture;
	}
}

function OnGUI() {
    if (GUI.Button(Rect(50,10,200,50),"Start WebCam")) {
        m_CameraTexture = new WebCamTexture(WebCamTexture.devices[0].name);
		if (m_CameraTexture != null) {
		      m_CameraTexture.Play();
		}
    }
}

Yep, sorry I wasn’t too clear about that SDK part.

It definitely shouldn’t be this hard to get the camera working so there’s a bug or two somewhere I agree. I guess another thing different from my setup is that I’m using C#, but this is just clutching at straws now.

Hope you get it fixed soon.

Hi MADmarine, thanks for giving more details. I wondered whether you could upload a working .apk file so we can test it on our devices? It would determine once and for all whether it’s a device problem or a build problem.

Thanks

I get the same black texture on my droid-X. Perhaps could you share your working android manifest?

With DDMS I do see some errors.

04-06 00:56:45.116: D/CameraSettings(1223): defaultCapabilities: preview-size-values: 176x144,320x240,352x288,640x480,720x480,800x448,1280x720
04-06 00:56:45.116: D/CameraSettings(1223): defaultCapabilities: preview-frame-rate-values: 5,10,15,20,24,25,28,30
04-06 00:56:45.123: D/CameraSettings(1223): defaultCapabilities: picture-format-values: jpeg,jfif,exif
04-06 00:56:45.123: D/CameraSettings(1223): defaultCapabilities: picture-size-values: 640x480,1280x960,1600x1200,2048x1536,2592x1936,3264x1840,3264x2448
04-06 00:56:45.123: D/CameraSettings(1223): defaultCapabilities: jpeg-thumbnail-size-values: 0x0,160x90,160x120,176x144,320x180,320x240
04-06 00:56:06.373: E/OMXCodec(1223): Successfully allocated software codec 'VorbisDecoder'
04-06 00:55:31.645: E/CameraSettings(1223): Param type 50 not supported
04-06 00:56:06.389: W/CameraHal(1223): No overlay set before start preview

I’ll try the thumbnail size to see if I get an image…

It sucks that the API doesn’t give access to the capabilities. Unless it does and I haven’t seen that doc yet…

This script works on my macbook pro, and on a toshiba thrive. No luck on droid-x. On the thrive, both cameras are detected.

using System;
using UnityEngine;
using System.Collections.Generic;

public class CameraTest : MonoBehaviour
{
    /// <summary>
    /// Meta reference to the camera
    /// </summary>
    public Material CameraMaterial = null;

    /// <summary>
    /// The number of frames per second
    /// </summary>
    private int m_framesPerSecond = 0;

    /// <summary>
    /// The current frame count
    /// </summary>
    private int m_frameCount = 0;

    /// <summary>
    /// The frames timer
    /// </summary>
    private DateTime m_timerFrames = DateTime.MinValue;

    /// <summary>
    /// The selected device index
    /// </summary>
    private int m_indexDevice = -1;

    /// <summary>
    /// The web cam texture
    /// </summary>
    private WebCamTexture m_texture = null;

    // Use this for initialization
    void Start()
    {
        if (null == CameraMaterial)
        {
            throw new ApplicationException("Missing camera material reference");
        }

        Application.RequestUserAuthorization(UserAuthorization.WebCam);
    }

    void OnGUI()
    {
        if (m_timerFrames < DateTime.Now)
        {
            m_framesPerSecond = m_frameCount;
            m_frameCount = 0;
            m_timerFrames = DateTime.Now + TimeSpan.FromSeconds(1);
        }
        ++m_frameCount;

        GUILayout.Label(string.Format("Frames per second: {0}", m_framesPerSecond));

        if (m_indexDevice >= 0 
            WebCamTexture.devices.Length > 0)
        {
            GUILayout.Label(string.Format("Selected Device: {0}", WebCamTexture.devices[m_indexDevice].name));
        }

        if (Application.HasUserAuthorization(UserAuthorization.WebCam))
        {
            GUILayout.Label("Has WebCam Authorization");
            if (null == WebCamTexture.devices)
            {
                GUILayout.Label("Null web cam devices");
            }
            else
            {
                GUILayout.Label(string.Format("{0} web cam devices", WebCamTexture.devices.Length));
                for (int index = 0; index < WebCamTexture.devices.Length; ++index)
                {
                    var device = WebCamTexture.devices[index];
                    if (string.IsNullOrEmpty(device.name))
                    {
                        GUILayout.Label("unnamed web cam device");
                        continue;
                    }

                    if (GUILayout.Button(string.Format("web cam device {0}{1}{2}",
                                                       m_indexDevice == index
                                                           ? "["
                                                           : string.Empty,
                                                       device.name,
                                                       m_indexDevice == index ? "]" : string.Empty),
                                         GUILayout.MinWidth(200),
                                         GUILayout.MinHeight(50)))
                    {
                        m_indexDevice = index;

                        // stop playing
                        if (null != m_texture)
                        {
                            if (m_texture.isPlaying)
                            {
                                m_texture.Stop();
                            }
                        }

                        // destroy the old texture
                        if (null != m_texture)
                        {
                            UnityEngine.Object.DestroyImmediate(m_texture, true);
                        }

                        // use the device name
                        m_texture = new WebCamTexture(device.name);

                        // start playing
                        m_texture.Play();

                        // assign the texture
                        CameraMaterial.mainTexture = m_texture;
                    }
                }
            }
        }
        else
        {
            GUILayout.Label("Pending WebCam Authorization...");
        }
    }

    // Update is called once per frame
    private void Update()
    {
        if (null != m_texture 
            m_texture.didUpdateThisFrame)
        {
            CameraMaterial.mainTexture = m_texture;
        }
    }
}

I get the same issues as tgraupmann, and can’t seem to find a solution either, other than looking elsewhere. Prime31s plugin works, but it launches an entirely seperate activity which isn’t what we want.

checking out the logcat, get interesting dump of camera stats, but nothing that hints at what maybe amiss, except the mysterious param 50, 51, and 53.

Condensed log output:
04-13 16:03:19.453: I/Unity(2365): Webcam permission = True
04-13 16:03:19.476: I/Unity(2365): Webcame devices = 1
04-13 16:03:19.508: I/Unity(2365): Cam back-facing device[0] = Camera 0
04-13 16:03:19.508: D/Unity(2365): Create: m_DeviceName=‘Camera 0’, device=0, w=0, h=0, fps=0
04-13 16:03:19.508: I/Unity(2365): getNumCameras: 1
04-13 16:03:19.508: I/Unity(2365): initCamera: 0
04-13 16:03:19.812: D/CameraCompModule(1208): Focus distance (0) = 3.151, Infinity, Infinity
04-13 16:03:19.828: D/Unity(2365): sz = 176x144
04-13 16:03:19.828: D/Unity(2365): sz = 320x240
04-13 16:03:19.828: D/Unity(2365): sz = 352x288
04-13 16:03:19.828: D/Unity(2365): sz = 640x480
04-13 16:03:19.828: D/Unity(2365): sz = 720x480
04-13 16:03:19.828: D/Unity(2365): sz = 800x448
04-13 16:03:19.828: D/Unity(2365): sz = 1280x720
04-13 16:03:19.844: D/CameraSettings(1208): Extract changes completed, 0 total changes
04-13 16:03:19.844: D/Unity(2365): cam[0]: size = 640x480; format=17, fps=(15000, 30000), bpp=12
04-13 16:03:19.844: D/CameraCompModule(1208): Focus distance (0) = 3.151, Infinity, Infinity
04-13 16:03:19.844: E/CameraSettings(1208): Param type 50 not supported
04-13 16:03:19.844: D/CameraHal(1208): focus-distance = 3.151, Infinity, Infinity
04-13 16:03:19.844: W/CameraHal(1208): No overlay set before start preview[/INDENT]