简体   繁体   English

Android Camera.takePicture失败了

[英]Android Camera.takePicture failed

As a bit of a learning exercise, I'm writing a security app that, when an arbitrary event happens, needs to turn the camera on, take a picture, and turn the camera off, without worrying about flash, focus, or displaying a preview. 作为一个学习练习,我正在编写一个安全应用程序,当发生任意事件时,需要打开相机,拍照并关闭相机,而不必担心闪光,对焦或显示预习。 I followed along the online demos and made a working app that takes a picture, but it uses previews and all that. 我跟着在线演示并制作了一个可以拍照的工作应用程序,但它使用预览和所有这些。 So I started working on getting it to work without a preview. 所以我开始努力让它在没有预览的情况下工作。 Anyway, I keep getting 'takePicture failed' exceptions, and I have no earthly idea why. 无论如何,我不断得到'takePicture failed'例外,我没有任何世俗的想法。 I was hoping someone with more experience with the Camera API could take a look and point me in the direction of a solution. 我希望有更多使用Camera API经验的人可以看看并指出我的解决方案。 Below are my pertinent files. 以下是我的相关文件。 I'm using the latest Android Studio and testing on Galaxy S4. 我正在使用最新的Android Studio并在Galaxy S4上进行测试。

[MainActivity.java] [MainActivity.java]

package com.g5digital.cam2;

import android.content.pm.PackageManager;
import android.hardware.Camera;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.Toast;

import java.io.IOException;

public class MainActivity extends ActionBarActivity implements View.OnClickListener {

    private static final String TAG = "MainActivity";
    private Button button;
    private int cameraId;
    private Camera camera;
    private CameraPreview camPreview;
    private LinearLayout container;
    private Camera.Parameters camParms;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        container = (LinearLayout)findViewById(R.id.container);

        button = (Button)findViewById(R.id.button);
        button.setOnClickListener(this);
    }


    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        if (id == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    @Override
    public void onClick(View view) {
        openCamera();

        try {
            if (camera != null) {
                PhotoHandler ph = new PhotoHandler(this, camera);
                camera.takePicture(null, null, ph);

                // Commented until takePicture() works
                /*(new Handler()).postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        MainActivity.this.closeCamera();
                    }
                }, 1000);*/
            }
        }
        catch (Exception e) {
            closeCamera();
            Log.d(TAG, e.getMessage());
            e.printStackTrace();
        }
    }

    private void openCamera() {
        // do we have a camera?
        if (!getPackageManager()
                .hasSystemFeature(PackageManager.FEATURE_CAMERA)) {
            Toast.makeText(this, "No camera on this device", Toast.LENGTH_LONG)
                    .show();
        }
        else {
            closeCamera();

            cameraId = findFrontFacingCameraId();
            if (cameraId < 0) {
                Toast.makeText(this, "No front facing camera found.",
                        Toast.LENGTH_LONG).show();
            } else {
                camera = Camera.open(cameraId);
                try {
                    setCamParms();

                    setCamPreview();
                    camera.startPreview();
                }
                catch (Exception e) {
                    closeCamera();
                    e.printStackTrace();
                    finish();
                    return;
                }
            }
        }
    }

    private void closeCamera() {
        if (camera != null) {
            camera.release();
            camera = null;
        }
    }

    private int findFrontFacingCameraId() {
        int camera_id = -1;
        // Search for the front facing camera
        int numberOfCameras = Camera.getNumberOfCameras();
        for (int i = 0; i < numberOfCameras; i++) {
            Camera.CameraInfo info = new Camera.CameraInfo();
            Camera.getCameraInfo(i, info);
            if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
                Log.d(TAG, "Camera found");
                camera_id = i;
                break;
            }
        }
        return camera_id;
    }

    private void setCamParms() {
        if (camParms == null && camera != null) {
            camParms = camera.getParameters();
            camParms.setFlashMode("Off");
        }
        if (camera != null) {
            camera.setParameters(camParms);
            camera.setDisplayOrientation(90);
        }
    }

    private void setCamPreview() throws IOException {
        if (camPreview == null && camera != null) {
            camPreview = new CameraPreview(this, camera);
        }
        if (camera != null) {
            camera.setPreviewDisplay(camPreview.getHolder());
        }
    }
}

[CameraPreview.java] [CameraPreview.java]

package com.g5digital.cam2;

import android.content.Context;
import android.hardware.Camera;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

import java.io.IOException;

public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
    private Context context;
    private Camera camera;
    private SurfaceHolder holder;
    private static final String TAG = "CameraPreview";

    public CameraPreview(Context c, Camera cam) {
        super(c);
        context = c;
        camera = cam;
        holder = getHolder();
        holder.addCallback(this);
    }

    @Override
    public void surfaceCreated(SurfaceHolder surfaceHolder) {
        try {
            camera.setPreviewDisplay(holder);
            camera.startPreview();
        } catch (IOException e) {
            Log.d(TAG, "Error setting camera preview: " + e.getMessage());
        } catch (Exception e) {
            // Probably getting "called after release()" message
            e.printStackTrace();
        }
    }

    @Override
    public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i2, int i3) {
        // If your preview can change or rotate, take care of those events here.
        // Make sure to stop the preview before resizing or reformatting it.

        if (holder.getSurface() == null){
            // preview surface does not exist
            return;
        }

        // stop preview before making changes
        try {
            camera.stopPreview();
        } catch (Exception e){
            // ignore: tried to stop a non-existent preview
        }

        // set preview size and make any resize, rotate or
        // reformatting changes here

        // start preview with new settings
        try {
            camera.setPreviewDisplay(holder);
            camera.startPreview();

        } catch (Exception e){
            Log.d(TAG, "Error starting camera preview: " + e.getMessage());
        }
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
        //
    }
}

[PhotoHandler.java] [PhotoHandler.java]

package com.g5digital.cam2;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.hardware.Camera;
import android.os.Environment;
import android.util.Log;
import android.widget.Toast;

import java.io.File;
import java.io.FileOutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;

public class PhotoHandler implements Camera.PictureCallback {
    private final Context context;
    private final Camera camera;

    public PhotoHandler(Context context, Camera c) {
        this.context = context;
        this.camera = c;
    }

    @Override
    public void onPictureTaken(byte[] bytes, Camera cam) {
        Log.i("PhotoHandler", "Picture taken!");
        File pictureFileDir = getDir();

        if (!pictureFileDir.exists() && !pictureFileDir.mkdirs()) {

            Log.d("PhotoHandler", "Can't create directory to save image.");
            Toast.makeText(context, "Can't create directory to save image.",
                    Toast.LENGTH_LONG).show();
            return;

        }

        Bitmap bmp = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
        int width = bmp.getWidth();
        int height = bmp.getHeight();
        Matrix matrix = new Matrix();
        matrix.postRotate(270);
        Bitmap rotatedBitmap = Bitmap.createBitmap(bmp, 0, 0,
                width, height, matrix, true);

        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
        String date = dateFormat.format(new Date());
        String photoFile = "Picture_" + date + ".jpg";

        String filename = pictureFileDir.getPath() + File.separator + photoFile;

        File pictureFile = new File(filename);

        try {
            FileOutputStream fos = new FileOutputStream(pictureFile);
            boolean result = rotatedBitmap.compress(Bitmap.CompressFormat.JPEG, 90, fos);
            //fos.write(bytes);
            fos.close();
            if (result) {
                Toast.makeText(context, "New Image saved:" + photoFile,
                        Toast.LENGTH_LONG).show();
            }
            else {
                Toast.makeText(context, "Couldn't save image:" + photoFile,
                        Toast.LENGTH_LONG).show();
            }
            camera.startPreview();
        } catch (Exception error) {
            Log.d("PhotoHandler", "File" + filename + "not saved: "
                    + error.getMessage());
            Toast.makeText(context, "Image could not be saved.",
                    Toast.LENGTH_LONG).show();
        }
    }

    private File getDir() {
        File sdDir = Environment
                .getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
        return new File(sdDir, "CameraAPIDemo");
    }
}

[AndroidManifest.xml] [AndroidManifest.xml中]

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.g5digital.cam2" >

    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.FLASHLIGHT" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-feature android:name="android.hardware.camera" />
    <uses-feature android:name="android.hardware.camera.autofocus" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.g5digital.cam2.MainActivity"
            android:label="@string/app_name"
            android:screenOrientation="portrait">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

[activity_main.xml] [activity_main.xml中]

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.g5digital.cam2.MainActivity"
    tools:ignore="MergeRootFrame">

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/click"
        android:id="@+id/button" />
</LinearLayout>

[LogCat output] [LogCat输出]

01-29 14:55:45.826    5853-5853/com.g5digital.cam2 D/MainActivity﹕ takePicture failed
01-29 14:55:45.826    5853-5853/com.g5digital.cam2 W/System.err﹕ java.lang.RuntimeException: takePicture failed
01-29 14:55:45.826    5853-5853/com.g5digital.cam2 W/System.err﹕ at android.hardware.Camera.native_takePicture(Native Method)
01-29 14:55:45.826    5853-5853/com.g5digital.cam2 W/System.err﹕ at android.hardware.Camera.takePicture(Camera.java:1194)
01-29 14:55:45.826    5853-5853/com.g5digital.cam2 W/System.err﹕ at android.hardware.Camera.takePicture(Camera.java:1139)
01-29 14:55:45.826    5853-5853/com.g5digital.cam2 W/System.err﹕ at com.g5digital.cam2.MainActivity.onClick(MainActivity.java:66)
01-29 14:55:45.826    5853-5853/com.g5digital.cam2 W/System.err﹕ at android.view.View.performClick(View.java:4475)
01-29 14:55:45.826    5853-5853/com.g5digital.cam2 W/System.err﹕ at android.view.View$PerformClick.run(View.java:18786)
01-29 14:55:45.826    5853-5853/com.g5digital.cam2 W/System.err﹕ at android.os.Handler.handleCallback(Handler.java:730)
01-29 14:55:45.826    5853-5853/com.g5digital.cam2 W/System.err﹕ at android.os.Handler.dispatchMessage(Handler.java:92)
01-29 14:55:45.826    5853-5853/com.g5digital.cam2 W/System.err﹕ at android.os.Looper.loop(Looper.java:137)
01-29 14:55:45.826    5853-5853/com.g5digital.cam2 W/System.err﹕ at android.app.ActivityThread.main(ActivityThread.java:5419)
01-29 14:55:45.826    5853-5853/com.g5digital.cam2 W/System.err﹕ at java.lang.reflect.Method.invokeNative(Native Method)
01-29 14:55:45.826    5853-5853/com.g5digital.cam2 W/System.err﹕ at java.lang.reflect.Method.invoke(Method.java:525)
01-29 14:55:45.826    5853-5853/com.g5digital.cam2 W/System.err﹕ at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1187)
01-29 14:55:45.826    5853-5853/com.g5digital.cam2 W/System.err﹕ at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
01-29 14:55:45.836    5853-5853/com.g5digital.cam2 W/System.err﹕ at dalvik.system.NativeStart.main(Native Method)

No, to take picture you must show preview. 不,拍照必须显示预览。 There are quite a few workarounds invented by skillful people, see eg Take Picture without preview Android , Android: Is it possible to take a picture with the camera from a service with no UI , How to use Camera to take picture in a background Service on Android? 熟练的人发明了相当多的变通方法,例如看看没有预览的拍照AndroidAndroid:可以用没有UI的服务用相机拍照如何使用相机在后台拍照服务Android的? ... ...

But keep in mind that the requirement is not for technical, but privacy purposes. 但请记住,要求不是出于技术要求,而是出于隐私目的。 And the system continues to evolve and protect against newly found workarounds. 系统继续发展并防范新发现的变通方法。

Maybe the most robust way to hide preview on S4 is to use SurfaceTexture, but display it invisibly, eg shifted off viewport. 也许在S4上隐藏预览的最强大的方法是使用SurfaceTexture,但是不可见地显示它,例如从视口移开。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM