El framework de Android admite diversas cámaras y características de cámara disponibles en los dispositivos. Esto permite capturar fotos y videos en las aplicaciones. En este documento, se brinda un enfoque rápido y simple sobre la captura de fotos y videos, y se proporciona un enfoque avanzado sobre la creación de experiencias de cámara personalizadas para los usuarios.
Nota: En esta página, se describe la clase Camera
, que quedó obsoleta. Recomendamos usar la biblioteca CameraX de Jetpack o, en casos de uso específicos, la clase camera2
. Tanto CameraX como Camera2 funcionan en Android 5.0 (nivel de API 21) y versiones posteriores.
Consulta los siguientes recursos relacionados:
Consideraciones
Antes de habilitar tu aplicación para el uso de cámaras en dispositivos Android, debes considerar algunas preguntas sobre la forma en que tu app pretende usar esta función de hardware.
- Requisito de cámara: ¿es el uso de la cámara tan importante para tu aplicación que no deseas que se instale en un dispositivo que no tenga una cámara? De ser así, debes declarar el requisito de cámara en el manifiesto.
- Foto rápida o Cámara personalizada: ¿de qué manera tu aplicación utilizará la cámara? ¿Solo deseas tomar una foto rápida o un videoclip? ¿O tu aplicación presentará una nueva forma de usar las cámaras? Para tomar una foto rápida o un clip, revisa Cómo usar las aplicaciones de cámara existentes. Para desarrollar una función de cámara personalizada, consulta la sección Cómo compilar una app de cámara.
- Requisito de servicios en primer plano: ¿cuándo interactúa tu app con la cámara? En Android 9 (nivel de API 28) y versiones posteriores, las apps que se ejecutan en segundo plano no pueden acceder a la cámara. Por lo tanto, debes usar la cámara cuando tu app se encuentre en primer plano o como parte de un servicio en primer plano.
- Almacenamiento: ¿las imágenes o los videos que genera la aplicación deben ser visibles únicamente para tu aplicación o pueden compartirse a fin de que otras aplicaciones, como Galería u otras apps de medios o de redes sociales, puedan usarlos? ¿Deseas que las fotos y los videos estén disponibles aunque se desinstale tu aplicación? Consulta la sección Cómo guardar archivos de medios para ver la forma de implementar estas opciones.
Conceptos básicos
El framework de Android admite la captura de imágenes y videos a través de la API de android.hardware.camera2
o la cámara Intent
. Estas son las clases relevantes:
android.hardware.camera2
- Este paquete es la API principal para controlar las cámaras de los dispositivos. Se puede utilizar para tomar fotos y videos al compilar una aplicación de cámara.
Camera
- Esta clase es la API obsoleta anterior para controlar las cámaras de los dispositivos.
SurfaceView
- Esta clase se utiliza para presentar una vista previa de la cámara en vivo al usuario.
MediaRecorder
- Esta clase se utiliza para grabar video de la cámara.
Intent
- Se puede usar un tipo de acción
MediaStore.ACTION_IMAGE_CAPTURE
oMediaStore.ACTION_VIDEO_CAPTURE
basada en intent para capturar imágenes o videos sin usar directamente el objetoCamera
.
Declaraciones del manifiesto
Antes de iniciar el desarrollo de una aplicación con la API de Camera, debes comprobar que el manifiesto contenga las declaraciones adecuadas para permitir el uso del hardware de cámara y otras características relacionadas.
- Permiso de cámara: tu aplicación debe solicitar permiso para usar una cámara del dispositivo.
<uses-permission android:name="android.permission.CAMERA" />
Nota: Si se invoca una app de cámara existente para usar la cámara, tu aplicación no necesita solicitar este permiso.
- Funciones de cámara: tu aplicación también debe declarar el uso de funciones de cámara, por ejemplo:
<uses-feature android:name="android.hardware.camera" />
Para ver una lista de las funciones de cámara, consulta el manifiesto Referencia de las funciones.
Al agregar funciones de cámara al manifiesto, Google Play impide que tu aplicación se instale en dispositivos que no incluyen una cámara o no admiten las funciones de cámara especificadas. Para obtener más información sobre el uso de filtros basados en funciones con Google Play, consulta Google Play y filtrado por funciones.
Si tu aplicación puede usar una cámara o una función de cámara para su correcto funcionamiento, pero no la requiere, debes especificar esto en el manifiesto. Para ello, incluye el atributo
android:required
y configúralo enfalse
:<uses-feature android:name="android.hardware.camera" android:required="false" />
- Permiso de almacenamiento: tu aplicación puede guardar imágenes o videos en el almacenamiento externo del dispositivo (tarjeta SD) si está orientada a Android 10 (nivel de API 29) o versiones anteriores y especifica lo siguiente en el manifiesto.
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
- Permiso de grabación de audio: tu aplicación debe solicitar el permiso de captura de audio para grabar audio con captura de video.
<uses-permission android:name="android.permission.RECORD_AUDIO" />
-
Permiso de ubicación: si tu aplicación etiqueta las imágenes con información de ubicación de GPS, debes solicitar el permiso
ACCESS_FINE_LOCATION
. Ten en cuenta que, si tu app se orienta a Android 5.0 (nivel de API 21) o versiones posteriores, también debes declarar que la app utiliza el GPS del dispositivo:<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> ... <!-- Needed only if your app targets Android 5.0 (API level 21) or higher. --> <uses-feature android:name="android.hardware.location.gps" />
Para obtener más información sobre la obtención de la ubicación del usuario, consulta Estrategias de ubicación.
Cómo usar las aplicaciones de cámara existentes
Una forma rápida de habilitar la captura de fotos o videos en una aplicación sin implementar demasiado código adicional es usar un objeto Intent
para invocar una aplicación de cámara Android existente.
Los detalles se describen en las lecciones de capacitación Cómo tomar fotos fácilmente y Cómo grabar videos fácilmente.
Cómo compilar una app de cámara
Algunos desarrolladores pueden necesitar una interfaz de usuario de cámara personalizada según el aspecto de su aplicación o con funciones especiales. La escritura de un código de captura de fotos propio puede brindar una experiencia más atractiva a los usuarios.
Nota: La siguiente guía es para la API anterior de Camera
, ya obsoleta. Para las aplicaciones de cámara nuevas o avanzadas, se recomienda usar la API de android.hardware.camera2
más reciente.
Los pasos generales para crear una interfaz de cámara personalizada de la aplicación son los siguientes:
- Detectar la cámara y obtener acceso: crea el código para comprobar la existencia de cámaras y solicitar acceso.
- Crear una clase de vista previa: crea una clase de vista previa de la cámara que extienda
SurfaceView
e implemente la interfazSurfaceHolder
. Esta clase brinda una vista previa de las imágenes en vivo de la cámara. - Crear un diseño de vista previa: después de obtener la clase de vista previa de la cámara, crea un diseño de vista donde se incorporen la vista previa y los controles de interfaz de usuario que desees.
- Configurar receptores para la captura: conecta receptores para que los controles de la interfaz inicien la captura de imágenes o videos en respuesta a acciones de los usuarios, como presionar un botón.
- Capturar y guardar archivos: configura el código para capturar fotos o videos y guardar la salida.
- Liberar la cámara: después de usar la cámara, la aplicación debe liberarla adecuadamente para que la usen otras aplicaciones.
El hardware de cámara es un recurso compartido que se debe administrar cuidadosamente para que la aplicación no entre en conflicto con otras aplicaciones que también deseen usarlo. En las siguientes secciones, se analiza cómo detectar el hardware de cámara, solicitar acceso a una cámara, capturar fotos o videos, y liberar la cámara cuando la aplicación termina de usarla.
Precaución: Recuerda liberar el objeto Camera
mediante una llamada a Camera.release()
cuando tu aplicación termine de usarlo. Si tu aplicación no libera adecuadamente la cámara, los intentos subsiguientes de acceder a la cámara, incluidos los de la propia aplicación, fallarán y tanto tu aplicación como otras podrían cerrarse.
Cómo detectar el hardware de cámara
Si tu aplicación no requiere específicamente una cámara mediante una declaración en el manifiesto, debes comprobar si existe una cámara disponible durante el tiempo de ejecución. Para realizar esta comprobación, utiliza el método PackageManager.hasSystemFeature()
, como se muestra en el siguiente código de ejemplo:
Kotlin
/** Check if this device has a camera */ private fun checkCameraHardware(context: Context): Boolean { if (context.packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA)) { // this device has a camera return true } else { // no camera on this device return false } }
Java
/** Check if this device has a camera */ private boolean checkCameraHardware(Context context) { if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)){ // this device has a camera return true; } else { // no camera on this device return false; } }
Los dispositivos Android pueden tener varias cámaras, por ejemplo, una posterior para fotografías y una frontal para videollamadas. Android 2.3 (nivel de API 9) y las versiones posteriores permiten comprobar la cantidad de cámaras disponibles en un dispositivo mediante el método Camera.getNumberOfCameras()
.
Cómo acceder a las cámaras
Si determinas que el dispositivo en el que se ejecuta tu aplicación tiene una cámara, debes solicitar acceso a esta. Para ello, debes obtener una instancia de Camera
(a menos que utilices un intent para acceder a la cámara).
Para acceder a la cámara principal, utiliza el método Camera.open()
y asegúrate de captar todas las excepciones, como se muestra en el código a continuación:
Kotlin
/** A safe way to get an instance of the Camera object. */ fun getCameraInstance(): Camera? { return try { Camera.open() // attempt to get a Camera instance } catch (e: Exception) { // Camera is not available (in use or does not exist) null // returns null if camera is unavailable } }
Java
/** A safe way to get an instance of the Camera object. */ public static Camera getCameraInstance(){ Camera c = null; try { c = Camera.open(); // attempt to get a Camera instance } catch (Exception e){ // Camera is not available (in use or does not exist) } return c; // returns null if camera is unavailable }
Precaución: Cuando uses Camera.open()
, busca siempre excepciones. Si no buscas las excepciones cuando la cámara se encuentra en uso o no existe, el sistema cerrará la aplicación.
En los dispositivos que ejecutan Android 2.3 (nivel de API 9) o una versión posterior, puedes acceder a cámaras específicas mediante Camera.open(int)
. El código de ejemplo anterior accederá a la primera cámara posterior de un dispositivo con más de una cámara.
Cómo comprobar las funciones de cámara
Después de acceder a una cámara, puedes obtener más información sobre sus capacidades utilizando el método Camera.getParameters()
y revisando el objeto Camera.Parameters
mostrado para ver las capacidades compatibles. Si usas el nivel de API 9 o superior, utiliza Camera.getCameraInfo()
para determinar si una cámara se encuentra en la parte frontal o posterior del dispositivo, y la orientación de la imagen.
Cómo crear una clase de vista previa
Para que los usuarios tomen fotos o videos con eficacia, deben poder ver lo que ve la cámara del dispositivo. Una clase de vista previa de la cámara es un elemento SurfaceView
que puede mostrar los datos de imagen en vivo procedentes de una cámara para que los usuarios puedan encuadrar y capturar una foto o un video.
En el siguiente código de ejemplo, se muestra la forma de crear una clase de vista previa de la cámara básica que se puede incluir en un diseño de View
. Esta clase implementa SurfaceHolder.Callback
para capturar los eventos de devolución de llamada destinados a crear y destruir la vista, lo que se necesita para asignar la entrada de vista previa de la cámara.
Kotlin
/** A basic Camera preview class */ class CameraPreview( context: Context, private val mCamera: Camera ) : SurfaceView(context), SurfaceHolder.Callback { private val mHolder: SurfaceHolder = holder.apply { // Install a SurfaceHolder.Callback so we get notified when the // underlying surface is created and destroyed. addCallback(this@CameraPreview) // deprecated setting, but required on Android versions prior to 3.0 setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS) } override fun surfaceCreated(holder: SurfaceHolder) { // The Surface has been created, now tell the camera where to draw the preview. mCamera.apply { try { setPreviewDisplay(holder) startPreview() } catch (e: IOException) { Log.d(TAG, "Error setting camera preview: ${e.message}") } } } override fun surfaceDestroyed(holder: SurfaceHolder) { // empty. Take care of releasing the Camera preview in your activity. } override fun surfaceChanged(holder: SurfaceHolder, format: Int, w: Int, h: Int) { // 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 (mHolder.surface == null) { // preview surface does not exist return } // stop preview before making changes try { mCamera.stopPreview() } catch (e: Exception) { // 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 mCamera.apply { try { setPreviewDisplay(mHolder) startPreview() } catch (e: Exception) { Log.d(TAG, "Error starting camera preview: ${e.message}") } } } }
Java
/** A basic Camera preview class */ public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback { private SurfaceHolder mHolder; private Camera mCamera; public CameraPreview(Context context, Camera camera) { super(context); mCamera = camera; // Install a SurfaceHolder.Callback so we get notified when the // underlying surface is created and destroyed. mHolder = getHolder(); mHolder.addCallback(this); // deprecated setting, but required on Android versions prior to 3.0 mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); } public void surfaceCreated(SurfaceHolder holder) { // The Surface has been created, now tell the camera where to draw the preview. try { mCamera.setPreviewDisplay(holder); mCamera.startPreview(); } catch (IOException e) { Log.d(TAG, "Error setting camera preview: " + e.getMessage()); } } public void surfaceDestroyed(SurfaceHolder holder) { // empty. Take care of releasing the Camera preview in your activity. } public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { // 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 (mHolder.getSurface() == null){ // preview surface does not exist return; } // stop preview before making changes try { mCamera.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 { mCamera.setPreviewDisplay(mHolder); mCamera.startPreview(); } catch (Exception e){ Log.d(TAG, "Error starting camera preview: " + e.getMessage()); } } }
Si deseas establecer un tamaño específico para la vista previa de la cámara, establece esto en el método surfaceChanged()
como se indica en los comentarios anteriores. Al establecer el tamaño de vista previa, debes usar valores de getSupportedPreviewSizes()
.
No establezcas valores arbitrarios en el método setPreviewSize()
.
Nota: Debido a la introducción de la función Multiventana en Android 7.0 (nivel de API 24) y versiones posteriores, ya no se puede asumir la relación de aspecto de la vista previa es la misma que la de tu actividad incluso después de llamar a setDisplayOrientation()
.
Según el tamaño de la ventana y la relación de aspecto, es posible que debas ajustar una vista previa de la cámara amplia a un diseño vertical, o viceversa, mediante un diseño letterbox (formato 16:9).
Cómo colocar la vista previa en un diseño
Para tomar una foto o un video, se debe colocar una clase de vista previa de la cámara, como la del ejemplo de la sección anterior, en el diseño de una actividad junto con los otros controles de interfaz de usuario. En esta sección, se muestra la forma de crear un diseño básico y la actividad para la vista previa.
El siguiente código de diseño brinda una vista sumamente básica que se puede utilizar para mostrar una vista previa de la cámara. En este ejemplo, el elemento FrameLayout
pretende ser el contenedor de la clase de vista previa de la cámara. Este tipo de diseño se utiliza para poder superponer información o controles de foto adicionales en las imágenes de vista previa de la cámara en vivo.
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="fill_parent" > <FrameLayout android:id="@+id/camera_preview" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_weight="1" /> <Button android:id="@+id/button_capture" android:text="Capture" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" /> </LinearLayout>
En la mayoría de los dispositivos, la orientación predeterminada de la vista previa de la cámara es horizontal. Este diseño de ejemplo especifica un diseño horizontal y el código debajo corrige la orientación de la aplicación a horizontal. Para simplificar la representación de una vista previa de la cámara, debes agregar lo siguiente a tu manifiesto a fin de cambiar la orientación de la actividad de vista previa de tu aplicación a horizontal.
<activity android:name=".CameraActivity" android:label="@string/app_name" android:screenOrientation="landscape"> <!-- configure this activity to use landscape orientation --> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>
Nota: Una vista previa de la cámara no necesita estar en modo horizontal.
A partir de Android 2.2 (nivel de API 8), puedes utilizar el método setDisplayOrientation()
para establecer la rotación de la imagen de vista previa. Para cambiar la orientación de la vista previa cuando el usuario gira el teléfono, dentro del método surfaceChanged()
de la clase de vista previa, debes detener la vista previa con Camera.stopPreview()
, cambiar la orientación y volver a iniciar la vista previa con Camera.startPreview()
.
En la actividad de la vista de cámara, agrega la clase de vista previa al elemento FrameLayout
que se muestra en el ejemplo anterior. La actividad de la cámara también debe garantizar la liberación de la cámara cuando esta se cierra o se pausa. En el siguiente ejemplo, se muestra la forma de modificar la actividad de una cámara para adjuntar la clase de vista previa mostrada en Cómo crear una clase de vista previa.
Kotlin
class CameraActivity : Activity() { private var mCamera: Camera? = null private var mPreview: CameraPreview? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // Create an instance of Camera mCamera = getCameraInstance() mPreview = mCamera?.let { // Create our Preview view CameraPreview(this, it) } // Set the Preview view as the content of our activity. mPreview?.also { val preview: FrameLayout = findViewById(R.id.camera_preview) preview.addView(it) } } }
Java
public class CameraActivity extends Activity { private Camera mCamera; private CameraPreview mPreview; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); // Create an instance of Camera mCamera = getCameraInstance(); // Create our Preview view and set it as the content of our activity. mPreview = new CameraPreview(this, mCamera); FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview); preview.addView(mPreview); } }
Nota: El método getCameraInstance()
del ejemplo anterior hace referencia al método de ejemplo que se muestra en Cómo acceder a las cámaras.
Cómo capturar fotos
Después de crear una clase de vista previa y un diseño de vista para mostrarla, estás listo para comenzar a capturar imágenes con la aplicación. En el código de la aplicación, debes configurar receptores para que los controles de la interfaz de usuario tomen una foto en respuesta a una acción del usuario.
Para recuperar una foto, utiliza el método Camera.takePicture()
. Este método toma tres parámetros que reciben datos de la cámara.
Para recibir los datos en formato JPEG, debes implementar una interfaz Camera.PictureCallback
a fin de recibir los datos de imagen y escribirlos en un archivo. En el siguiente código, se muestra una implementación básica de la interfaz Camera.PictureCallback
para guardar una imagen recibida de la cámara.
Kotlin
private val mPicture = Camera.PictureCallback { data, _ -> val pictureFile: File = getOutputMediaFile(MEDIA_TYPE_IMAGE) ?: run { Log.d(TAG, ("Error creating media file, check storage permissions")) return@PictureCallback } try { val fos = FileOutputStream(pictureFile) fos.write(data) fos.close() } catch (e: FileNotFoundException) { Log.d(TAG, "File not found: ${e.message}") } catch (e: IOException) { Log.d(TAG, "Error accessing file: ${e.message}") } }
Java
private PictureCallback mPicture = new PictureCallback() { @Override public void onPictureTaken(byte[] data, Camera camera) { File pictureFile = getOutputMediaFile(MEDIA_TYPE_IMAGE); if (pictureFile == null){ Log.d(TAG, "Error creating media file, check storage permissions"); return; } try { FileOutputStream fos = new FileOutputStream(pictureFile); fos.write(data); fos.close(); } catch (FileNotFoundException e) { Log.d(TAG, "File not found: " + e.getMessage()); } catch (IOException e) { Log.d(TAG, "Error accessing file: " + e.getMessage()); } } };
Llama al método Camera.takePicture()
para activar la captura de una imagen. En el siguiente código de ejemplo, se muestra la forma de llamar a este método desde un botón View.OnClickListener
.
Kotlin
val captureButton: Button = findViewById(R.id.button_capture) captureButton.setOnClickListener { // get an image from the camera mCamera?.takePicture(null, null, picture) }
Java
// Add a listener to the Capture button Button captureButton = (Button) findViewById(R.id.button_capture); captureButton.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { // get an image from the camera mCamera.takePicture(null, null, picture); } } );
Nota: El miembro mPicture
del siguiente ejemplo hace referencia al código de ejemplo anterior.
Precaución: Recuerda liberar el objeto Camera
mediante una llamada a Camera.release()
cuando tu aplicación termine de usarlo. Para obtener información sobre la forma de liberar la cámara, consulta Cómo liberar la cámara.
Cómo capturar videos
La captura de videos mediante el framework de Android requiere una administración atenta del objeto Camera
y una coordinación con la clase MediaRecorder
. Cuando grabas videos con Camera
, debes administrar las llamadas Camera.lock()
y Camera.unlock()
para permitir el acceso de MediaRecorder
al hardware de cámara, además de las llamadas Camera.open()
y Camera.release()
.
Nota: A partir de Android 4.0 (nivel de API 14), las llamadas Camera.lock()
y Camera.unlock()
se administran de automáticamente.
A diferencia de la toma de fotos con la cámara de un dispositivo, la captura de video requiere un orden de llamadas especial. Es necesario seguir un orden específico de ejecución para prepararse correctamente y capturar el video con la aplicación, tal como se detalla a continuación.
- Abrir la cámara: utiliza
Camera.open()
para obtener una instancia del objeto de cámara. - Conectar la vista previa: conecta
SurfaceView
a la cámara medianteCamera.setPreviewDisplay()
para preparar una vista previa de imagen de la cámara en vivo. - Iniciar la vista previa: llama a
Camera.startPreview()
para comenzar a mostrar las imágenes de la cámara en vivo. - Iniciar la grabación de video: es necesario completar los siguientes pasos para grabar correctamente un video:
- Desbloquear la cámara: desbloquea la cámara para que la use
MediaRecorder
mediante una llamada aCamera.unlock()
. - Configurar MediaRecorder: llama a los siguientes métodos
MediaRecorder
en este orden. Para obtener más información, consulta la documentación de referencia deMediaRecorder
.setCamera()
: configura la cámara para usarla en la captura de video; utiliza la instancia actual deCamera
de la aplicación.setAudioSource()
: configura la fuente de audio; utilizaMediaRecorder.AudioSource.CAMCORDER
.setVideoSource()
: configura la fuente de video; utilizaMediaRecorder.VideoSource.CAMERA
.- Establece el formato de salida de video y la codificación. Para Android 2.2 (nivel de API 8) y versiones posteriores, utiliza el método
MediaRecorder.setProfile
y obtén una instancia de perfil medianteCamcorderProfile.get()
. Para las versiones de Android anteriores a la 2.2, debes establecer los parámetros de formato de salida de video y codificación:setOutputFormat()
: establece el formato de salida; especifica el valor predeterminado oMediaRecorder.OutputFormat.MPEG_4
.setAudioEncoder()
: establece el tipo de codificación de sonido; especifica el valor predeterminado oMediaRecorder.AudioEncoder.AMR_NB
.setVideoEncoder()
: establece el tipo de codificación de video; especifica el valor predeterminado oMediaRecorder.VideoEncoder.MPEG_4_SP
.
setOutputFile()
: establece el archivo de salida; utilizagetOutputMediaFile(MEDIA_TYPE_VIDEO).toString()
del método de ejemplo en la sección Cómo guardar archivos de medios.setPreviewDisplay()
: especifica el elemento de diseño de vista previaSurfaceView
para la aplicación. Utiliza el mismo objeto especificado para Conectar la vista previa.
Precaución: Debes llamar a estos métodos de configuración
MediaRecorder
en este orden; de lo contrario, tu aplicación experimentará errores y fallará la grabación. - Preparar MediaRecorder: prepara
MediaRecorder
con los valores de configuración proporcionados mediante una llamada aMediaRecorder.prepare()
. - Iniciar MediaRecorder: inicia la grabación de video mediante una llamada a
MediaRecorder.start()
.
- Desbloquear la cámara: desbloquea la cámara para que la use
- Detener la grabación de video: llama a los siguientes métodos en orden para completar correctamente una grabación de video:
- Detener MediaRecorder: detén la grabación de video mediante una llamada a
MediaRecorder.stop()
. - Restablecer MediaRecorder: de forma opcional, puedes quitar los valores de configuración del grabador mediante una llamada a
MediaRecorder.reset()
. - Liberar MediaRecorder: libera
MediaRecorder
mediante una llamada aMediaRecorder.release()
. - Bloquear la cámara: bloquea la cámara para que las sesiones futuras de
MediaRecorder
puedan usarla mediante una llamada aCamera.lock()
. A partir de Android 4.0 (nivel de API 14), no se requiere esta llamada, a menos que la llamada aMediaRecorder.prepare()
falle.
- Detener MediaRecorder: detén la grabación de video mediante una llamada a
- Detener la vista previa: cuando hayas terminado de usar la cámara, detén la vista previa mediante
Camera.stopPreview()
. - Liberar la cámara: libera la cámara para que otras aplicaciones puedan usarla mediante una llamada a
Camera.release()
.
Nota: Es posible usar MediaRecorder
sin crear una vista previa de la cámara antes y omitir los primeros pasos de este proceso. Sin embargo, como los usuarios generalmente prefieren obtener una vista previa antes de iniciar una grabación, ese proceso no se describe aquí.
Sugerencia: Si tu aplicación se usa principalmente para grabar video, establece setRecordingHint(boolean)
en true
antes de iniciar la vista previa. Esta configuración ayuda a reducir el tiempo que se requiere para iniciar la grabación.
Cómo configurar MediaRecorder
Al usar la clase MediaRecorder
para grabar video, primero es necesario realizar los pasos de configuración en un orden específico y, posteriormente, llamar al método MediaRecorder.prepare()
para comprobar e implementar la configuración. En el siguiente código de ejemplo, se muestra la forma de configurar correctamente y preparar la clase MediaRecorder
para la grabación de video.
Kotlin
private fun prepareVideoRecorder(): Boolean { mediaRecorder = MediaRecorder() mCamera?.let { camera -> // Step 1: Unlock and set camera to MediaRecorder camera?.unlock() mediaRecorder?.run { setCamera(camera) // Step 2: Set sources setAudioSource(MediaRecorder.AudioSource.CAMCORDER) setVideoSource(MediaRecorder.VideoSource.CAMERA) // Step 3: Set a CamcorderProfile (requires API Level 8 or higher) setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH)) // Step 4: Set output file setOutputFile(getOutputMediaFile(MEDIA_TYPE_VIDEO).toString()) // Step 5: Set the preview output setPreviewDisplay(mPreview?.holder?.surface) setOutputFormat(MediaRecorder.OutputFormat.MPEG_4) setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT) setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT) // Step 6: Prepare configured MediaRecorder return try { prepare() true } catch (e: IllegalStateException) { Log.d(TAG, "IllegalStateException preparing MediaRecorder: ${e.message}") releaseMediaRecorder() false } catch (e: IOException) { Log.d(TAG, "IOException preparing MediaRecorder: ${e.message}") releaseMediaRecorder() false } } } return false }
Java
private boolean prepareVideoRecorder(){ mCamera = getCameraInstance(); mediaRecorder = new MediaRecorder(); // Step 1: Unlock and set camera to MediaRecorder mCamera.unlock(); mediaRecorder.setCamera(mCamera); // Step 2: Set sources mediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER); mediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); // Step 3: Set a CamcorderProfile (requires API Level 8 or higher) mediaRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH)); // Step 4: Set output file mediaRecorder.setOutputFile(getOutputMediaFile(MEDIA_TYPE_VIDEO).toString()); // Step 5: Set the preview output mediaRecorder.setPreviewDisplay(mPreview.getHolder().getSurface()); // Step 6: Prepare configured MediaRecorder try { mediaRecorder.prepare(); } catch (IllegalStateException e) { Log.d(TAG, "IllegalStateException preparing MediaRecorder: " + e.getMessage()); releaseMediaRecorder(); return false; } catch (IOException e) { Log.d(TAG, "IOException preparing MediaRecorder: " + e.getMessage()); releaseMediaRecorder(); return false; } return true; }
Antes de Android 2.2 (nivel de API 8), debes establecer los parámetros de formato de salida de video y codificación directamente, en lugar de usar CamcorderProfile
. Este enfoque se muestra en el siguiente código:
Kotlin
// Step 3: Set output format and encoding (for versions prior to API Level 8) mediaRecorder?.apply { setOutputFormat(MediaRecorder.OutputFormat.MPEG_4) setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT) setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT) }
Java
// Step 3: Set output format and encoding (for versions prior to API Level 8) mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT); mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT);
Los siguientes parámetros de grabación de video para MediaRecorder
reciben valores de configuración predeterminados; sin embargo, se recomienda ajustar estos valores según la aplicación:
setVideoEncodingBitRate()
setVideoSize()
setVideoFrameRate()
setAudioEncodingBitRate()
setAudioChannels()
setAudioSamplingRate()
Cómo iniciar y detener MediaRecorder
Al iniciar y detener la grabación de video mediante la clase MediaRecorder
, debes seguir el orden específico que se indica a continuación.
- Desbloquear la cámara con
Camera.unlock()
- Configurar
MediaRecorder
como se muestra en el ejemplo de código anterior - Iniciar la grabación mediante
MediaRecorder.start()
- Grabar el video
- Detener la grabación mediante
MediaRecorder.stop()
- Liberar el grabador de medios con
MediaRecorder.release()
- Bloquear la cámara mediante
Camera.lock()
En el siguiente código de ejemplo, se muestra la forma de conectar un botón para iniciar y detener correctamente la grabación de video mediante la cámara y la clase MediaRecorder
.
Nota: Al completar una grabación de video, no liberes la cámara; si lo haces, se detendrá la vista previa.
Kotlin
var isRecording = false val captureButton: Button = findViewById(R.id.button_capture) captureButton.setOnClickListener { if (isRecording) { // stop recording and release camera mediaRecorder?.stop() // stop the recording releaseMediaRecorder() // release the MediaRecorder object mCamera?.lock() // take camera access back from MediaRecorder // inform the user that recording has stopped setCaptureButtonText("Capture") isRecording = false } else { // initialize video camera if (prepareVideoRecorder()) { // Camera is available and unlocked, MediaRecorder is prepared, // now you can start recording mediaRecorder?.start() // inform the user that recording has started setCaptureButtonText("Stop") isRecording = true } else { // prepare didn't work, release the camera releaseMediaRecorder() // inform user } } }
Java
private boolean isRecording = false; // Add a listener to the Capture button Button captureButton = (Button) findViewById(id.button_capture); captureButton.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { if (isRecording) { // stop recording and release camera mediaRecorder.stop(); // stop the recording releaseMediaRecorder(); // release the MediaRecorder object mCamera.lock(); // take camera access back from MediaRecorder // inform the user that recording has stopped setCaptureButtonText("Capture"); isRecording = false; } else { // initialize video camera if (prepareVideoRecorder()) { // Camera is available and unlocked, MediaRecorder is prepared, // now you can start recording mediaRecorder.start(); // inform the user that recording has started setCaptureButtonText("Stop"); isRecording = true; } else { // prepare didn't work, release the camera releaseMediaRecorder(); // inform user } } } } );
Nota: En el ejemplo anterior, el método prepareVideoRecorder()
hace referencia al código de ejemplo que se muestra en Cómo configurar MediaRecorder. Este método se ocupa de bloquear la cámara, realizar la configuración y preparar la instancia de MediaRecorder
.
Cómo liberar la cámara
Las cámaras son un recurso que comparten las aplicaciones de un dispositivo. La aplicación puede hacer uso de la cámara después de obtener una instancia de Camera
; es necesario prestar especial atención a liberar el objeto de cámara cuando la aplicación deja de usarlo y tan pronto como la aplicación se pone en pausa (Activity.onPause()
). Si tu aplicación no libera adecuadamente la cámara, los intentos subsiguientes de acceder a la cámara, incluidos los de la propia aplicación, fallarán y tanto tu aplicación como otras podrían cerrarse.
Para liberar una instancia del objeto Camera
, utiliza el método Camera.release()
, tal como se muestra en el código de ejemplo anterior.
Kotlin
class CameraActivity : Activity() { private var mCamera: Camera? private var preview: SurfaceView? private var mediaRecorder: MediaRecorder? override fun onPause() { super.onPause() releaseMediaRecorder() // if you are using MediaRecorder, release it first releaseCamera() // release the camera immediately on pause event } private fun releaseMediaRecorder() { mediaRecorder?.reset() // clear recorder configuration mediaRecorder?.release() // release the recorder object mediaRecorder = null mCamera?.lock() // lock camera for later use } private fun releaseCamera() { mCamera?.release() // release the camera for other applications mCamera = null } }
Java
public class CameraActivity extends Activity { private Camera mCamera; private SurfaceView preview; private MediaRecorder mediaRecorder; ... @Override protected void onPause() { super.onPause(); releaseMediaRecorder(); // if you are using MediaRecorder, release it first releaseCamera(); // release the camera immediately on pause event } private void releaseMediaRecorder(){ if (mediaRecorder != null) { mediaRecorder.reset(); // clear recorder configuration mediaRecorder.release(); // release the recorder object mediaRecorder = null; mCamera.lock(); // lock camera for later use } } private void releaseCamera(){ if (mCamera != null){ mCamera.release(); // release the camera for other applications mCamera = null; } } }
Precaución: Si tu aplicación no libera adecuadamente la cámara, los intentos subsiguientes de acceder a la cámara, incluidos los de la propia aplicación, fallarán y tanto tu aplicación como otras podrían cerrarse.
Cómo guardar archivos de medios
Los archivos de medios que crean los usuarios, como fotos y videos, deben guardarse en el directorio de almacenamiento externo de un dispositivo (tarjeta SD) para ahorrar espacio del sistema y permitir que los usuarios accedan a estos archivos sin su dispositivo. Existen numerosas ubicaciones de directorio posibles para guardar los archivos de medios en un dispositivo. Sin embargo, son solo dos las ubicaciones estándar que debes considerar como desarrollador:
Environment.getExternalStoragePublicDirectory
(Environment.DIRECTORY_PICTURES
): este método muestra la ubicación estándar, compartida y recomendada para guardar fotos y videos. Este directorio es compartido (público), por lo que las aplicaciones pueden detectar, leer, modificar y borrar de forma fácil los archivos guardados en esta ubicación. Si el usuario desinstala la aplicación, no se borran los archivos de medios guardados en esta ubicación. A fin de evitar interferir en las fotos y los videos existentes de los usuarios, debes crear un subdirectorio para los archivos de medios de la aplicación dentro de este directorio, como se muestra en el ejemplo de código anterior. Este método se encuentra disponible en Android 2.2 (nivel de API 8); para ver las llamadas equivalentes en versiones de API anteriores, consulta Guardar archivos compartidos.Context.getExternalFilesDir
(Environment.DIRECTORY_PICTURES
): este método muestra una ubicación estándar para guardar las fotos y los videos asociados con la aplicación. Si se desinstala la aplicación, se quitan todos los archivos guardados en esta ubicación. No se aplican medidas de seguridad sobre los archivos de esta ubicación, por lo que otras aplicaciones pueden leerlos, modificarlos y borrarlos.
En el siguiente código de ejemplo, se muestra la forma de crear una ubicación File
o Uri
para un archivo de medios que se puede usar al invocar la cámara de un dispositivo con un objeto Intent
o al compilar una app de cámara.
Kotlin
val MEDIA_TYPE_IMAGE = 1 val MEDIA_TYPE_VIDEO = 2 /** Create a file Uri for saving an image or video */ private fun getOutputMediaFileUri(type: Int): Uri { return Uri.fromFile(getOutputMediaFile(type)) } /** Create a File for saving an image or video */ private fun getOutputMediaFile(type: Int): File? { // To be safe, you should check that the SDCard is mounted // using Environment.getExternalStorageState() before doing this. val mediaStorageDir = File( Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "MyCameraApp" ) // This location works best if you want the created images to be shared // between applications and persist after your app has been uninstalled. // Create the storage directory if it does not exist mediaStorageDir.apply { if (!exists()) { if (!mkdirs()) { Log.d("MyCameraApp", "failed to create directory") return null } } } // Create a media file name val timeStamp = SimpleDateFormat("yyyyMMdd_HHmmss").format(Date()) return when (type) { MEDIA_TYPE_IMAGE -> { File("${mediaStorageDir.path}${File.separator}IMG_$timeStamp.jpg") } MEDIA_TYPE_VIDEO -> { File("${mediaStorageDir.path}${File.separator}VID_$timeStamp.mp4") } else -> null } }
Java
public static final int MEDIA_TYPE_IMAGE = 1; public static final int MEDIA_TYPE_VIDEO = 2; /** Create a file Uri for saving an image or video */ private static Uri getOutputMediaFileUri(int type){ return Uri.fromFile(getOutputMediaFile(type)); } /** Create a File for saving an image or video */ private static File getOutputMediaFile(int type){ // To be safe, you should check that the SDCard is mounted // using Environment.getExternalStorageState() before doing this. File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory( Environment.DIRECTORY_PICTURES), "MyCameraApp"); // This location works best if you want the created images to be shared // between applications and persist after your app has been uninstalled. // Create the storage directory if it does not exist if (! mediaStorageDir.exists()){ if (! mediaStorageDir.mkdirs()){ Log.d("MyCameraApp", "failed to create directory"); return null; } } // Create a media file name String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()); File mediaFile; if (type == MEDIA_TYPE_IMAGE){ mediaFile = new File(mediaStorageDir.getPath() + File.separator + "IMG_"+ timeStamp + ".jpg"); } else if(type == MEDIA_TYPE_VIDEO) { mediaFile = new File(mediaStorageDir.getPath() + File.separator + "VID_"+ timeStamp + ".mp4"); } else { return null; } return mediaFile; }
Nota: Environment.getExternalStoragePublicDirectory()
está disponible en Android 2.2 (nivel de API 8) o versiones posteriores. Si deseas apuntar a dispositivos con versiones anteriores de Android, utiliza Environment.getExternalStorageDirectory()
en su lugar. Para obtener más información, consulta Cómo guardar archivos compartidos.
Para lograr que el URI admita perfiles de trabajo, primero debes convertir el URI de archivo en un URI de contenido. Luego, debes agregar el URI de contenido a EXTRA_OUTPUT
en un Intent
.
Para obtener más información sobre la forma de guardar archivos en un dispositivo Android, consulta Almacenamiento de datos.
Funciones de cámara
Android admite una amplia gama de funciones de cámara que puedes controlar con tu aplicación de cámara, como el formato de foto, el modo de flash, los ajustes de enfoque y muchas más. En esta sección, se enumeran las funciones de cámara más comunes y se describe brevemente la forma de usarlas. El acceso y la configuración de la mayoría de las funciones de cámara se pueden lograr mediante el objeto Camera.Parameters
. Sin embargo, existen varias funciones importantes para las que se necesita más que una simple configuración en Camera.Parameters
. Estas funciones se abordan en las siguientes secciones:
Para obtener información general sobre la forma de usar las funciones controladas mediante Camera.Parameters
, revisa la sección Cómo usar funciones de la cámara. Para obtener información detallada sobre la forma de usar las funciones controladas mediante el objeto de parámetro de cámara, sigue los vínculos a la documentación de referencia de la API en la lista de funciones que aparece a continuación.
Tabla 1: Funciones de cámara comunes ordenadas según el nivel de API de Android en el que se presentaron
Función | Nivel de API | Descripción |
---|---|---|
Detección de rostro | 14 | Identificar rostros humanos dentro de una foto y usarlos para enfoque, medición y balance de blancos |
Áreas de medición | 14 | Especificar una o varias áreas dentro de una imagen para calcular el balance de blancos |
Áreas de interés | 14 | Establecer una o varias áreas dentro de una imagen para usar como enfoque |
White Balance Lock |
14 | Detener o iniciar los ajustes automáticos de balance de blancos |
Exposure Lock |
14 | Detener o iniciar los ajustes automáticos de exposición |
Video Snapshot |
14 | Tomar una foto mientras se graba un video (captura de fotograma) |
Video en time lapse | 11 | Grabar fotogramas con intervalos de retraso para registrar un video en time lapse |
Multiple Cameras |
9 | Admitir más de una cámara en un dispositivo, incluidas las cámaras frontales y las posteriores |
Focus Distance |
9 | Informar distancias entre la cámara y los objetos que parecen estar en foco |
Zoom |
8 | Establecer la ampliación de la imagen |
Exposure
Compensation |
8 | Aumentar o disminuir el nivel de exposición a la luz |
GPS Data |
5 | Incluir u omitir datos de ubicación geográfica en la imagen |
White Balance |
5 | Establecer el modo de balance de blancos, lo que afecta los valores de color en la imagen capturada |
Focus Mode |
5 | Establecer el modo en que la cámara se enfoca en un objeto, por ejemplo, automático, fijo, macro o infinito |
Scene Mode |
5 | Aplicar un modo preestablecido para tipos específicos de situaciones de fotografía, como escenas nocturnas, en la playa, en la nieve o a la luz de las velas |
JPEG Quality |
5 | Establecer el nivel de compresión de una imagen JPEG, lo que aumenta o disminuye la calidad y el tamaño del archivo de salida de imagen |
Flash Mode |
5 | Activar y desactivar el flash, o usar la configuración automática |
Color Effects |
5 | Aplicar un efecto de color sobre una imagen capturada, por ejemplo, blanco y negro, sepia o negativo |
Anti-Banding |
5 | Reducir el efecto de bandas en gradientes de color debido a la compresión JPEG |
Picture Format |
1 | Especificar el formato de archivo para la foto |
Picture Size |
1 | Especificar las dimensiones en píxeles de la foto guardada |
Nota: Estas funciones no son compatibles con todos los dispositivos debido a las diferencias de hardware y la implementación de software. Para obtener información sobre la disponibilidad de funciones en el dispositivo donde se ejecuta la aplicación, consulta Cómo comprobar la disponibilidad de funciones.
Cómo comprobar la disponibilidad de funciones
Lo primero que es necesario comprender al comenzar a usar funciones de cámara en dispositivos Android es que no todas las funciones son compatibles con todos los dispositivos. Asimismo, los dispositivos compatibles con una función determinada pueden admitirla en diferentes niveles o con diferentes opciones. Por lo tanto, una parte del proceso de toma de decisiones como desarrollador durante el desarrollo de una aplicación de cámara es determinar qué funciones de cámara deseas admitir y en qué nivel. Una vez que tomes esa decisión, debes planear la incorporación de un código en la aplicación de cámara para comprobar si el hardware del dispositivo admite esas funciones y genera un error digno cuando la función no está disponible.
Para comprobar la disponibilidad de las funciones de cámara, puedes obtener una instancia de un objeto de parámetro de cámara y revisar los métodos correspondientes. En el siguiente ejemplo de código, se muestra la forma de obtener un objeto Camera.Parameters
y comprobar si la cámara admite la función de enfoque automático:
Kotlin
val params: Camera.Parameters? = camera?.parameters val focusModes: List<String>? = params?.supportedFocusModes if (focusModes?.contains(Camera.Parameters.FOCUS_MODE_AUTO) == true) { // Autofocus mode is supported }
Java
// get Camera parameters Camera.Parameters params = camera.getParameters(); List<String> focusModes = params.getSupportedFocusModes(); if (focusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) { // Autofocus mode is supported }
Puedes usar la técnica anterior con la mayoría de las funciones de cámara. El
El objeto Camera.Parameters
proporciona un método getSupported...()
, is...Supported()
o getMax...()
para determinar si (y en qué medida) un atributo es
no es compatible.
Si la aplicación requiere ciertas funciones de cámara para funcionar correctamente, puedes exigirlas a través de adiciones al manifiesto de la aplicación. Cuando se declara el uso de funciones de cámara específicas, como el flash y el enfoque automático, Google Play limita la instalación de la aplicación en dispositivos que no admiten estas funciones. Para obtener una lista de las funciones de cámara que se pueden declarar en el manifiesto de tu app, consulta el manifiesto Referencia de las funciones.
Cómo usar las funciones de cámara
La mayoría de las funciones de cámara se activan y se controlan mediante un objeto Camera.Parameters
. Para obtener este objeto, primero debes obtener una instancia del objeto Camera
, llamar al método getParameters()
, cambiar el objeto de parámetro mostrado y, luego, volver a establecerlo en el objeto de cámara, como se muestra en el siguiente código de ejemplo:
Kotlin
val params: Camera.Parameters? = camera?.parameters params?.focusMode = Camera.Parameters.FOCUS_MODE_AUTO camera?.parameters = params
Java
// get Camera parameters Camera.Parameters params = camera.getParameters(); // set the focus mode params.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO); // set Camera parameters camera.setParameters(params);
Esta técnica funciona con casi todas las funciones de cámara. La mayoría de los parámetros se pueden cambiar en cualquier momento después de obtener una instancia del objeto Camera
. Normalmente, el usuario puede ver los cambios en los parámetros de inmediato en la vista previa de la cámara de la aplicación.
En el software, la implementación de los cambios en los parámetros puede llevar varios fotogramas, ya que el hardware de la cámara debe procesar las nuevas instrucciones y enviar los datos de imagen actualizados.
Importante: Algunas funciones de la cámara no se pueden cambiar en el futuro. En especial, el cambio de la orientación o del tamaño de la vista previa de la cámara requiere primero detener la vista previa, cambiar el tamaño de la vista previa y reiniciar la vista previa. A partir de Android 4.0 (nivel de API 14), es posible cambiar la orientación de la vista previa sin reiniciar la vista previa.
Otras funciones de cámara requieren más código para su implementación. Algunas de estas son las siguientes:
- Áreas de enfoque y medición
- Detección de rostro
- Video en time lapse
En las siguientes secciones, se proporciona una descripción rápida de la forma de implementar estas funciones.
Áreas de enfoque y medición
En algunos escenarios fotográficos, la medición de la luz y el enfoque automáticos pueden no producir los resultados deseados. A partir de Android 4.0 (nivel de API 14), tu aplicación de cámara puede proporcionar controles adicionales para que tu app o los usuarios puedan especificar áreas específicas de una imagen y usarlas con el objetivo de determinar los ajustes de enfoque o nivel de luz y transferir estos valores al hardware de la cámara a fin de usarlos en la captura de imágenes o videos.
Las áreas de medición y enfoque funcionan de forma muy similar a otras funciones de cámara en el sentido de que se controlan a través de métodos en el objeto Camera.Parameters
. En el siguiente código, se muestra la configuración de dos áreas de medición de luz para una instancia de Camera
:
Kotlin
// Create an instance of Camera camera = getCameraInstance() // set Camera parameters val params: Camera.Parameters? = camera?.parameters params?.apply { if (maxNumMeteringAreas > 0) { // check that metering areas are supported meteringAreas = ArrayList<Camera.Area>().apply { val areaRect1 = Rect(-100, -100, 100, 100) // specify an area in center of image add(Camera.Area(areaRect1, 600)) // set weight to 60% val areaRect2 = Rect(800, -1000, 1000, -800) // specify an area in upper right of image add(Camera.Area(areaRect2, 400)) // set weight to 40% } } camera?.parameters = this }
Java
// Create an instance of Camera camera = getCameraInstance(); // set Camera parameters Camera.Parameters params = camera.getParameters(); if (params.getMaxNumMeteringAreas() > 0){ // check that metering areas are supported List<Camera.Area> meteringAreas = new ArrayList<Camera.Area>(); Rect areaRect1 = new Rect(-100, -100, 100, 100); // specify an area in center of image meteringAreas.add(new Camera.Area(areaRect1, 600)); // set weight to 60% Rect areaRect2 = new Rect(800, -1000, 1000, -800); // specify an area in upper right of image meteringAreas.add(new Camera.Area(areaRect2, 400)); // set weight to 40% params.setMeteringAreas(meteringAreas); } camera.setParameters(params);
El objeto Camera.Area
contiene dos parámetros de datos: un objeto Rect
para especificar un área dentro del campo visual de la cámara y un valor de peso, que indica a la cámara el nivel de importancia que esta área debe tener en la medición de la luz o los cálculos de foco.
El campo Rect
de un objeto Camera.Area
describe una forma rectangular mapeada en una cuadrícula de 2000 x 2000 unidades. Las coordenadas -1000, -1000 representan la esquina superior izquierda de la imagen de la cámara, y las coordenadas 1000, 1000 representan la esquina inferior derecha de la imagen de la cámara, como se muestra en la ilustración a continuación.
Figura 1: las líneas rojas ilustran el sistema de coordenadas para especificar un elemento Camera.Area
dentro de la vista previa de la cámara. El cuadro azul muestra la ubicación y la forma de un área de cámara con los valores de Rect
333,333,667,667.
Los límites de este sistema de coordenadas siempre coinciden con el borde externo de la imagen visible en la vista previa de la cámara y no se contraen ni se expanden con el nivel de zoom. De forma similar, la rotación de la vista previa de la imagen mediante Camera.setDisplayOrientation()
no vuelve a mapear el sistema de coordenadas.
Detección de rostro
En las fotos con personas, los rostros generalmente son la parte más importante de la imagen y se deben usar para determinar el enfoque y el balance de blancos cuando se captura una imagen. El framework de Android 4.0 (nivel de API 14) proporciona diversas API para identificar rostros y calcular los ajustes de la foto mediante tecnología de reconocimiento facial.
Nota: Mientras se ejecuta la función de detección de rostro setWhiteBalance(String)
, setFocusAreas(List<Camera.Area>)
y setMeteringAreas(List<Camera.Area>)
no tienen efecto alguno.
Para usar la función de detección de rostro en la aplicación de cámara, se requieren algunos pasos generales:
- Comprobar que la detección de rostro sea compatible con el dispositivo
- Crear un objeto de escucha de detección de rostro
- Agregar el objeto de escucha de detección de rostro al objeto de cámara
- Iniciar la detección de rostro después de la vista previa (y después de cada reinicio de la vista previa)
La función de detección de rostro no es compatible con todos los dispositivos. Para comprobar si esta función es compatible, llama a getMaxNumDetectedFaces()
. En el método de ejemplo de startFaceDetection()
que aparece a continuación, se muestra un ejemplo de esta comprobación.
Para recibir un aviso y responder a la detección de un rostro, la aplicación de cámara debe establecer un receptor de los eventos de detección de rostro. Con tal fin, debes crear una clase de receptor que implemente la interfaz de Camera.FaceDetectionListener
como se muestra en el siguiente código de ejemplo.
Kotlin
internal class MyFaceDetectionListener : Camera.FaceDetectionListener { override fun onFaceDetection(faces: Array<Camera.Face>, camera: Camera) { if (faces.isNotEmpty()) { Log.d("FaceDetection", ("face detected: ${faces.size}" + " Face 1 Location X: ${faces[0].rect.centerX()}" + "Y: ${faces[0].rect.centerY()}")) } } }
Java
class MyFaceDetectionListener implements Camera.FaceDetectionListener { @Override public void onFaceDetection(Face[] faces, Camera camera) { if (faces.length > 0){ Log.d("FaceDetection", "face detected: "+ faces.length + " Face 1 Location X: " + faces[0].rect.centerX() + "Y: " + faces[0].rect.centerY() ); } } }
Después de crear esta clase, debes incluirla en el objeto Camera
de la aplicación, como se muestra en el siguiente código de ejemplo:
Kotlin
camera?.setFaceDetectionListener(MyFaceDetectionListener())
Java
camera.setFaceDetectionListener(new MyFaceDetectionListener());
La aplicación debe iniciar la función de detección de rostro cada vez que se inicia (o reinicia) la vista previa de la cámara. Crea un método para iniciar la detección de rostro y llamarla según sea necesario, como se muestra en el siguiente código de ejemplo.
Kotlin
fun startFaceDetection() { // Try starting Face Detection val params = mCamera?.parameters // start face detection only *after* preview has started params?.apply { if (maxNumDetectedFaces > 0) { // camera supports face detection, so can start it: mCamera?.startFaceDetection() } } }
Java
public void startFaceDetection(){ // Try starting Face Detection Camera.Parameters params = mCamera.getParameters(); // start face detection only *after* preview has started if (params.getMaxNumDetectedFaces() > 0){ // camera supports face detection, so can start it: mCamera.startFaceDetection(); } }
Debes iniciar la detección de rostro cada vez que inicias (o reinicias) la vista previa de la cámara. Si utilizas la clase de vista previa que se muestra en Cómo crear una clase de vista previa, agrega el método startFaceDetection()
a los métodos surfaceCreated()
y surfaceChanged()
de la clase de vista previa, como se muestra en el siguiente código de ejemplo.
Kotlin
override fun surfaceCreated(holder: SurfaceHolder) { try { mCamera.setPreviewDisplay(holder) mCamera.startPreview() startFaceDetection() // start face detection feature } catch (e: IOException) { Log.d(TAG, "Error setting camera preview: ${e.message}") } } override fun surfaceChanged(holder: SurfaceHolder, format: Int, w: Int, h: Int) { if (holder.surface == null) { // preview surface does not exist Log.d(TAG, "holder.getSurface() == null") return } try { mCamera.stopPreview() } catch (e: Exception) { // ignore: tried to stop a non-existent preview Log.d(TAG, "Error stopping camera preview: ${e.message}") } try { mCamera.setPreviewDisplay(holder) mCamera.startPreview() startFaceDetection() // re-start face detection feature } catch (e: Exception) { // ignore: tried to stop a non-existent preview Log.d(TAG, "Error starting camera preview: ${e.message}") } }
Java
public void surfaceCreated(SurfaceHolder holder) { try { mCamera.setPreviewDisplay(holder); mCamera.startPreview(); startFaceDetection(); // start face detection feature } catch (IOException e) { Log.d(TAG, "Error setting camera preview: " + e.getMessage()); } } public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { if (holder.getSurface() == null){ // preview surface does not exist Log.d(TAG, "holder.getSurface() == null"); return; } try { mCamera.stopPreview(); } catch (Exception e){ // ignore: tried to stop a non-existent preview Log.d(TAG, "Error stopping camera preview: " + e.getMessage()); } try { mCamera.setPreviewDisplay(holder); mCamera.startPreview(); startFaceDetection(); // re-start face detection feature } catch (Exception e){ // ignore: tried to stop a non-existent preview Log.d(TAG, "Error starting camera preview: " + e.getMessage()); } }
Nota: Recuerda llamar a este método después de llamar a startPreview()
. No intentes iniciar la detección de rostro en el método onCreate()
de la actividad principal de la app de cámara, ya que la vista previa no estará disponible en este punto de la ejecución de la aplicación.
Video en time lapse
Con el video en time lapse, los usuarios pueden crear videoclips compuestos por fotos tomadas con unos pocos segundos o minutos de diferencia. Esta función utiliza MediaRecorder
para registrar las imágenes en una secuencia de intervalo de tiempo.
Para grabar un video en time lapse con MediaRecorder
, debes configurar el objeto grabador como si grabaras un video normal, pero debes establecer los fotogramas capturados por segundo en un valor menor y usar uno de los ajustes de calidad de intervalo de tiempo, como se muestra en el ejemplo de código a continuación.
Kotlin
mediaRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_TIME_LAPSE_HIGH)) mediaRecorder.setCaptureRate(0.1) // capture a frame every 10 seconds
Java
// Step 3: Set a CamcorderProfile (requires API Level 8 or higher) mediaRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_TIME_LAPSE_HIGH)); ... // Step 5.5: Set the video capture rate to a low number mediaRecorder.setCaptureRate(0.1); // capture a frame every 10 seconds
Es necesario determinar estos ajustes como parte de un procedimiento de configuración mayor para MediaRecorder
. Si quieres obtener un ejemplo de código de configuración total, consulta Cómo configurar MediaRecorder. Una vez completada la configuración, inicia la grabación de video como si grabaras un videoclip normal. Para obtener más información sobre la configuración y la ejecución de MediaRecorder
, consulta Cómo capturar videos.
En los ejemplos de Camera2Video y HdrViewfinder, se demuestra aún más el uso de las API que se abordan en esta página.
Campos de cámara que requieren permiso
Las apps que ejecutan Android 10 (nivel de API 29) o versiones posteriores deben tener el permiso CAMERA
para acceder a los valores de los siguientes campos que muestra el método getCameraCharacteristics()
:
LENS_POSE_ROTATION
LENS_POSE_TRANSLATION
LENS_INTRINSIC_CALIBRATION
LENS_RADIAL_DISTORTION
LENS_POSE_REFERENCE
LENS_DISTORTION
LENS_INFO_HYPERFOCAL_DISTANCE
LENS_INFO_MINIMUM_FOCUS_DISTANCE
SENSOR_REFERENCE_ILLUMINANT1
SENSOR_REFERENCE_ILLUMINANT2
SENSOR_CALIBRATION_TRANSFORM1
SENSOR_CALIBRATION_TRANSFORM2
SENSOR_COLOR_TRANSFORM1
SENSOR_COLOR_TRANSFORM2
SENSOR_FORWARD_MATRIX1
SENSOR_FORWARD_MATRIX2
Código de ejemplo adicional
Para descargar apps de ejemplo, consulta Ejemplo de Camera2Basic y App de ejemplo de CameraX oficial.