Capturar vídeo y reproducirlo

La aplicación que realizaremos nos permitirá grabar un vídeo, tener una vista previa mientras se graba y luego reproducirlo. Queremos que al finalizar se vea así:

Disposición inicial

Iniciamos descargando el código que debe ser importado hacia un nuevo proyecto, la configuración inicial para este ejemplo tiene algunas características importantes para revisar:

  • Configuración de la rotación de pantalla: para evitarnos problemas porque cada vez que se mueve el teléfono y rota la pantalla se destruye y re-crea la activity, en el manifiesto le hemos configurado que no pueda rotarse con android:screenOrientation="portrait" dentro de la etiqueta de la activity principal.
  • Permisos en el manifiesto: por la utilización de la cámara para grabar audio y vídeo al SD requerimos de estos cuatro permisos:
  • <uses-permission android:name="android.permission.CAMERA" />   
    
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    
    <uses-permission android:name="android.permission.RECORD_VIDEO" />
    
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    
  • En el archivo principal de diseño res/layout/main.xml incluimos un LinearLayout.
  • En la clase de la activity principal, implementaremos la interfaz SurfaceHolder.Callback para el manejo del SurfaceView que se agregará más adelante. Esta interfaz requiere la implementación de los siguientes métodos.
    @Override
    public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
    }
    @Override
    public void surfaceCreated(SurfaceHolder arg0) {
    }
    @Override
    public void surfaceDestroyed(SurfaceHolder arg0) {
    }
    

Diseño

Seguimos trabajando sobre un LinearLayout y agregamos un nuevo elemento:

  • SurfaceView: nos permite tener un espacio dedicado a dibujar los frames del vídeo para la vista y la reproducción.

Nuestro diseño tendrá tres botones: “Grabar”, “Detener” y “Reproducir” además del ya mencionado SurfaceView El código completo del layout es:

<!--?xml version="1.0" encoding="utf-8"?-->
<button>
	</button>
	<button>
	</button>
	<button>
	</button>

Agregando código para funcionalidad

Vamos a definir 4 variables globales, las primeras dos son para gestionar la grabación y reproducción:

private MediaRecorder mediaRecorder = null;
private MediaPlayer mediaPlayer = null;

También tendremos una tercera variable de instancia para el nombre del archivo en el que se va a escribir:

private String fileName = null;

La cuarta variable de instancia para controlar cuando se está grabando:

private boolean recording = false;

De los métodos heredados por la interface nos interesan 2: surfaceDestroyed donde vamos a liberar los recursos y surfaceCreated donde vamos a inicializar.

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
	mediaRecorder.release();
	mediaPlayer.release();
}

Verificamos si las variables son nulas (para ejecutar este código sólo una vez) y luego de inicializarlas se coloca el SurfaceHolder como display para la vista previa de la grabación y para la vista de la reproducción:

@Override
public void surfaceCreated(SurfaceHolder holder) {
	if (mediaRecorder == null) {
		mediaRecorder = new MediaRecorder();
		mediaRecorder.setPreviewDisplay(holder.getSurface());
	}

	if (mediaPlayer == null) {
		mediaPlayer = new MediaPlayer();
		mediaPlayer.setDisplay(holder);
	}
}

Adicionalmente, se agrega un método para preparar la grabación configurando los atributos de la fuente para audio y vídeo, el formado y el codificador.

public void prepareRecorder(){
	mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
	mediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
	mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);      mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);
	mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.MPEG_4_SP);
}

Dentro del método onCreate realizamos algunas inicializaciones, primero para la variable del nombre del archivo:

fileName = Environment.getExternalStorageDirectory() + "/test.mp4";

También para el SurfaceView donde se reproducirá el vídeo:

SurfaceView surface = (SurfaceView)findViewById(R.id.surface);
SurfaceHolder holder = surface.getHolder();
holder.addCallback(this);
holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

Definimos los botones sobre los que vamos a trabajar en su evento de click:

final Button btnRec = (Button)findViewById(R.id.btnRec);
final Button btnStop = (Button)findViewById(R.id.btnStop);
final Button btnPlay = (Button)findViewById(R.id.btnPlay);

Botón de grabación: al iniciar deshabilitamos los botones de grabar y reproducir luego habilitamos el de detener. Llamamos el método que configura el MediaRecorder y le decimos el archivo de salida. Una vez configurado todo llamamos al método prepare que deja todo listo para iniciar la grabación e iniciamos la grabación y actualizamos el estatus de la variable recording.

btnRec.setOnClickListener(new OnClickListener() {
	@Override
	public void onClick(View v) {
			btnRec.setEnabled(false);
			btnStop.setEnabled(true);
			btnPlay.setEnabled(false);
			prepareRecorder();
			mediaRecorder.setOutputFile(fileName);
			try {
				mediaRecorder.prepare();
			} catch (IllegalStateException e) {
			} catch (IOException e) {
			}

			mediaRecorder.start();
			recording = true;
	}
});

Botón detener: habilitamos los botones de grabar y reproducir, deshabilitamos el de detener. Si se está grabando, detenemos la grabación y reiniciamos la configuración además de volver falsa la variable de estatus de grabación. Si se está reproduciendo, detenemos la reproducción y reiniciamos la configuración:

btnStop.setOnClickListener(new OnClickListener() {
	@Override
	public void onClick(View v) {
		btnRec.setEnabled(true);
		btnStop.setEnabled(false);
		btnPlay.setEnabled(true);
		if (recording) {
			recording = false;
			mediaRecorder.stop();
			mediaRecorder.reset();
		} else if (mediaPlayer.isPlaying()) {
			mediaPlayer.stop();
			mediaPlayer.reset();
		}
	}
});

Botón de reproducir: deshabilitamos los botones de grabar, reproducir y habilitamos el de detener. Si concluye la reproducción (porque el video se acabó no porque el usuario haya presionado detener) habilitamos los botones de grabar, reproducir y deshabilitamos el de detener). Luego configuramos el archivo a partir del cual se reproducirá, preparamos el Media Player e iniciamos la reproducción.

btnPlay.setOnClickListener(new OnClickListener() {
	@Override
	public void onClick(View v) {
		btnRec.setEnabled(false);
		btnStop.setEnabled(true);
		btnPlay.setEnabled(false);

		mediaPlayer.setOnCompletionListener(new OnCompletionListener() {
			@Override
			public void onCompletion(MediaPlayer mp) {

				btnRec.setEnabled(true);
				btnStop.setEnabled(false);
				btnPlay.setEnabled(true);
			}
		});

		try {
			mediaPlayer.setDataSource(fileName);
			mediaPlayer.prepare();
		} catch (IllegalStateException e) {
		} catch (IOException e) {
		}

		mediaPlayer.start();

	}
});

Descarga:

Puedes descargar el código de la aplicación completa y funcional en: Grabación y reproducción de vídeo.

Conclusión

  • SurfaceView: utilizamos este tipo de view que permite el manejo de gráficas y provee una forma de dibujar, en nuestro caso las imágenes necesarias para la vista previa del video y luego para su reproducción.
  • Manejo y configuración de Media Recorder: para la captura de video, aprendimos a inicializar, configurar y utilizar el Media Recorder.
  • Manejo y configuración de Media Player: una vez grabado un video, el Media Player nos sirvió para reproducirlo.
  • Utilización de la cámara de vídeo: hicimos uso de la cámara de nuevo pero ahora para la grabación de video, de una manera similar que en el caso de las fotos (guía anterior) no manejamos directamente el hardware si no que a través de las herramientas que nos provee Android utilizamos el componente sin complicaciones.