Pregunta

Estoy escribiendo una aplicación de Android 1.5 que comienza justo después del arranque. Se trata de un Service y debe tomar una foto sin vista previa. Esta aplicación va a registrar la densidad de la luz en algunas zonas lo que sea. Pude tomar una foto pero la imagen era negro.

Después de investigar durante mucho tiempo, me encontré con un hilo de errores al respecto. Si no genere una vista previa, la imagen será de color negro desde la cámara de Android necesita de vista previa para la exposición y el enfoque de instalación. He creado un SurfaceView y el oyente, pero el evento onSurfaceCreated() no es despedido.

Creo que la razón es, la superficie no está siendo creado visualmente. También he visto algunos ejemplos de llamar a la cámara estática con MediaStore.CAPTURE_OR_SOMETHING que toma una fotografía y la guarda en la carpeta deseada con dos líneas de código, pero no hace falta ser un cuadro demasiado.

es necesario utilizar IPC y bindService() llamar a esta función I? O hay un método alternativo para lograr esto?

¿Fue útil?

Solución

es muy raro que la cámara en la plataforma Android no puede transmitir vídeo hasta que se da vista previa superficie válida. parece que los arquitectos de la plataforma no estaba pensando tercera parte de vídeo streaming de aplicaciones en absoluto. incluso para el caso de realidad aumentada de la imagen puede ser presentada como una especie de sustitución visual, no corriente de la cámara en tiempo real.

de todos modos, sólo tiene que superficie de cambio de tamaño de vista previa para 1x1 píxeles y ponerlo en algún lugar en la esquina del widget (elemento visual). por favor, preste atención -. superficie de cambio de tamaño de vista previa, no el tamaño marco de la cámara

Por supuesto, tales truco no elimina los datos no deseados de streaming (para la vista previa) que consume algunos recursos del sistema y de la batería.

Otros consejos

He encontrado la respuesta a esto en el Android Cámara Docs .

  

Nota: Es posible utilizar MediaRecorder sin crear una cámara   previsualizar primero y saltar los primeros pasos de este proceso. Sin embargo,   ya que los usuarios generalmente prefieren ver una vista previa antes de iniciar una   grabación, ese proceso no se discute aquí.

Se puede encontrar la instrucciones paso a paso en el enlace anterior. Después de las instrucciones, se indicará la cita que he proporcionado anteriormente.

En realidad es posible, pero hay que falsificar la vista previa con un maniquí SurfaceView

SurfaceView view = new SurfaceView(this);
c.setPreviewDisplay(view.getHolder());
c.startPreview();
c.takePicture(shutterCallback, rawPictureCallback, jpegPictureCallback);

Actualización 09/21/11:. Al parecer esto no funciona para todos los dispositivos Android

Tomando la foto

Recibe esta trabajando primero antes de tratar de ocultar la vista previa.

  • Ajuste correctamente la vista previa
    • Usar un SurfaceView (pre-Android-4,0 compatibilidad) o SurfaceTexture (Android 4+, se puede hacer transparente)
    • Establecer e inicializa antes de tomar la foto
    • Espere a SurfaceView del SurfaceHolder (a través de getHolder()) que informe surfaceCreated() o la TextureView a onSurfaceTextureAvailable informe a su SurfaceTextureListener antes de la inicialización y la vista previa.
  • Asegúrese de que la vista previa es visible:
    • Añadir a la WindowManager
    • Asegúrese de que su tamaño de diseño es de al menos 1x1 píxeles (debería poner a empezar por lo que es MATCH_PARENT x MATCH_PARENT para probar)
    • Asegúrese de que su visibilidad es View.VISIBLE (que parece ser el valor por defecto si no se especifica que)
    • Asegúrese de utilizar el FLAG_HARDWARE_ACCELERATED en el LayoutParams si es un TextureView.
  • JPEG de devolución de llamada de Uso takePicture ya que la documentación dice las otras devoluciones de llamada no son compatibles con todos los dispositivos

Solución de problemas

  • Si surfaceCreated / onSurfaceTextureAvailable no quede llama, no se está visualizando el SurfaceView / TextureView probablemente.
  • Si takePicture falla, primero asegurar la visión preliminar está funcionando correctamente. Puede eliminar su llamada takePicture y dejar que la vista previa de ejecución para ver si se muestra en la pantalla.
  • Si la imagen es más oscuro de lo que debería ser, puede que tenga que retrasar durante un segundo antes de llamar takePicture para que la cámara tiene tiempo para ajustar su exposición una vez que ha comenzado la vista previa.

Cómo ocultar la vista previa

  • Haga el tamaño de vista previa View 1x1 para minimizar su visibilidad ( o tratar de 8x16 posiblemente más fiabilidad )

    new WindowManager.LayoutParams(1, 1, /*...*/)
    
  • Mover la vista previa fuera del centro para reducir su perceptibilidad:

    new WindowManager.LayoutParams(width, height,
        Integer.MIN_VALUE, Integer.MIN_VALUE, /*...*/)
    
  • Hacer la vista previa transparente (sólo funciona para TextureView)

    WindowManager.LayoutParams params = new WindowManager.LayoutParams(
        width, height, /*...*/
        PixelFormat.TRANSPARENT);
    params.alpha = 0;
    

Ejemplo de trabajo (probado en Sony Xperia M, Android 4.3)

/** Takes a single photo on service start. */
public class PhotoTakingService extends Service {

    @Override
    public void onCreate() {
        super.onCreate();
        takePhoto(this);
    }

    @SuppressWarnings("deprecation")
    private static void takePhoto(final Context context) {
        final SurfaceView preview = new SurfaceView(context);
        SurfaceHolder holder = preview.getHolder();
        // deprecated setting, but required on Android versions prior to 3.0
        holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

        holder.addCallback(new Callback() {
            @Override
            //The preview must happen at or after this point or takePicture fails
            public void surfaceCreated(SurfaceHolder holder) {
                showMessage("Surface created");

                Camera camera = null;

                try {
                    camera = Camera.open();
                    showMessage("Opened camera");

                    try {
                        camera.setPreviewDisplay(holder);
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }

                    camera.startPreview();
                    showMessage("Started preview");

                    camera.takePicture(null, null, new PictureCallback() {

                        @Override
                        public void onPictureTaken(byte[] data, Camera camera) {
                            showMessage("Took picture");
                            camera.release();
                        }
                    });
                } catch (Exception e) {
                    if (camera != null)
                        camera.release();
                    throw new RuntimeException(e);
                }
            }

            @Override public void surfaceDestroyed(SurfaceHolder holder) {}
            @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}
        });

        WindowManager wm = (WindowManager)context
            .getSystemService(Context.WINDOW_SERVICE);
        WindowManager.LayoutParams params = new WindowManager.LayoutParams(
                1, 1, //Must be at least 1x1
                WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
                0,
                //Don't know if this is a safe default
                PixelFormat.UNKNOWN);

        //Don't set the preview visibility to GONE or INVISIBLE
        wm.addView(preview, params);
    }

    private static void showMessage(String message) {
        Log.i("Camera", message);
    }

    @Override public IBinder onBind(Intent intent) { return null; }
}

En Android 4.0 y superior (nivel de API> = 14), se puede usar TextureView de vista previa de la corriente de la cámara y que sea tan invisible como a lo muestre al usuario. He aquí cómo:

En primer lugar crear una clase para implementar un SurfaceTextureListener que hará que las devoluciones de llamada Crear / Actualizar para la superficie de vista previa. Esta clase también toma un objeto de la cámara como entrada, de modo que pueda llamar a la función startPreview de la cámara tan pronto como se crea la superficie:

public class CamPreview extends TextureView implements SurfaceTextureListener {

  private Camera mCamera;

  public CamPreview(Context context, Camera camera) {
    super(context);
    mCamera = camera;
   }

  @Override
  public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
    Camera.Size previewSize = mCamera.getParameters().getPreviewSize();
    setLayoutParams(new FrameLayout.LayoutParams(
        previewSize.width, previewSize.height, Gravity.CENTER));

    try{
      mCamera.setPreviewTexture(surface);
     } catch (IOException t) {}

    mCamera.startPreview();
    this.setVisibility(INVISIBLE); // Make the surface invisible as soon as it is created
  }

  @Override
  public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
      // Put code here to handle texture size change if you want to
  }

  @Override
  public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
    return true;
  }

  @Override
  public void onSurfaceTextureUpdated(SurfaceTexture surface) {
      // Update your view here!
  }
}

También tendrá que implementar una clase de devolución de llamada para procesar los datos de vista previa:

public class CamCallback implements Camera.PreviewCallback{
  public void onPreviewFrame(byte[] data, Camera camera){
     // Process the camera data here
  }
}

El uso de lo anterior y clases CamPreview CamCallback para configurar la cámara en onCreate de su actividad () o la función de arranque similar:

// Setup the camera and the preview object
Camera mCamera = Camera.open(0);
CamPreview camPreview = new CamPreview(Context,mCamera);
camPreview.setSurfaceTextureListener(camPreview);

// Connect the preview object to a FrameLayout in your UI
// You'll have to create a FrameLayout object in your UI to place this preview in
FrameLayout preview = (FrameLayout) findViewById(R.id.cameraView); 
preview.addView(camPreview);

// Attach a callback for preview
CamCallback camCallback = new CamCallback();
mCamera.setPreviewCallback(camCallback);

Hay una manera de hacer esto, pero es un poco complicado. lo que debe hacerse, es adjuntar un surfaceholder al gestor de ventanas del servicio

WindowManager wm = (WindowManager) mCtx.getSystemService(Context.WINDOW_SERVICE);
params = new WindowManager.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT,
            WindowManager.LayoutParams.WRAP_CONTENT,
            WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
            WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
            PixelFormat.TRANSLUCENT);        
wm.addView(surfaceview, params);

a continuación, establezca

surfaceview.setZOrderOnTop(true);
mHolder.setFormat(PixelFormat.TRANSPARENT);

donde mHolder es el soporte que recibe de la vista de la superficie.

De esta manera, se puede jugar con el alfa de SurfaceView, que sea completamente transparente, pero aun así la cámara obtendrá marcos.

así es como lo hago. creo que sirve:)

Hemos resuelto este problema mediante el uso de un SurfaceView ficticia (no se añade a la GUI real) en versiones inferiores a 3.0 (o digamos 4.0 como un servicio de cámara en una tableta realmente no tiene sentido). En las versiones> = 4.0 Esto funcionó sólo en el emulador; ( El uso de SurfaceTexture (y setSurfaceTexture ()) en lugar de SurfaceView (y setSurfaceView ()) trabajó aquí. Al menos esto funciona en Nexus S.

creo que esto realmente es una deficiencia del marco de Android.

En el "Ejemplo de trabajo por Sam" (Gracias Sam ...)

Si en istruction "wm.addView (vista previa, params);"

obtener excepción "No se puede agregar ventana android.view.ViewRoot - ha denegado el permiso para este tipo de ventana"

resuelven mediante el uso de este permiso en AndroidManifest:

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>

Puede probar el código de trabajo, este servicio haga clic en la imagen frontal, si quieres captura de imagen de la cámara en ese entonces backCamera elimine el comentario de código y comentarios frontCamera.

Nota: - Permitir la cámara y el permiso de almacenamiento de APP y StartService de actividad o en cualquier lugar.

public class MyService extends Service {

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        CapturePhoto();
    }

    private void CapturePhoto() {

        Log.d("kkkk","Preparing to take photo");
        Camera camera = null;

        Camera.CameraInfo cameraInfo = new Camera.CameraInfo();

            int frontCamera = 1;
            //int backCamera=0;

            Camera.getCameraInfo(frontCamera, cameraInfo);

            try {
                camera = Camera.open(frontCamera);
            } catch (RuntimeException e) {
                Log.d("kkkk","Camera not available: " + 1);
                camera = null;
                //e.printStackTrace();
            }
            try {
                if (null == camera) {
                    Log.d("kkkk","Could not get camera instance");
                } else {
                    Log.d("kkkk","Got the camera, creating the dummy surface texture");
                     try {
                         camera.setPreviewTexture(new SurfaceTexture(0));
                        camera.startPreview();
                    } catch (Exception e) {
                        Log.d("kkkk","Could not set the surface preview texture");
                        e.printStackTrace();
                    }
                    camera.takePicture(null, null, new Camera.PictureCallback() {

                        @Override
                        public void onPictureTaken(byte[] data, Camera camera) {
                            File pictureFileDir=new File("/sdcard/CaptureByService");

                            if (!pictureFileDir.exists() && !pictureFileDir.mkdirs()) {
                                pictureFileDir.mkdirs();
                            }
                            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyymmddhhmmss");
                            String date = dateFormat.format(new Date());
                            String photoFile = "ServiceClickedPic_" + "_" + date + ".jpg";
                            String filename = pictureFileDir.getPath() + File.separator + photoFile;
                            File mainPicture = new File(filename);

                            try {
                                FileOutputStream fos = new FileOutputStream(mainPicture);
                                fos.write(data);
                                fos.close();
                                Log.d("kkkk","image saved");
                            } catch (Exception error) {
                                Log.d("kkkk","Image could not be saved");
                            }
                            camera.release();
                        }
                    });
                }
            } catch (Exception e) {
                camera.release();
            }
    }
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top