Android y JavaCV: Grabar videos con la cámara



Si hay una diferencia entre Android e iOS realmente objetiva es el tema de las cámaras de videos. En iOS existen multitud de cámaras con diversos efectos especiales y únicos, mientras que en Android nos resignamos a los pocos que vienen de fábrica dependiendo de la versión de Android que tengas. Y el efecto sepia no mola.


Y con la amplia gama de dispositivos Android, de los cuales más de uno cuentan con mejores procesadores y cámaras que la gama iPhone, ¿cómo se pueden hacer  aplicaciones de cámaras con efectos especiales al igual que existen en iPhone?:
Pues oficialmente no se puede, el firmware en Android funciona de otra forma que impide este tipo de aplicaciones. Si cuentas con acceso root, podrías intentar hacer algo con el Framebuffer... pero eso sería pasar al lado oscuro... (y no tengo nada en contra excepto que si te saltas las "reglas" es cuando comienzan los errores inesperados porque cada Android es diferente).


 Hay dos formas de grabar vídeos en Android "oficialmente":

  • Lanzando un Intent: lo cual es como pedirle al usuario que si quiere grabar un video abra la aplicación de la cámara y que por favor lo grabe. 
  • Con las clases Camera y MediaRecorder: Te permiten grabar un video, controlando algunos parámetros como la resolución, el zoom... y puedes aplicar unos pocos efectos predefinidos (según versión, pero casualmente siempre podrás aplicar el efecto sepia :( ).
Oficialmente no puedes "meter código" entre la cámara y el codificador de video, por lo que no se puede alterar las imágenes antes de que se graben en el archivo de video y por lo tanto la tarea de hacer efectos de video se complica.

¿Pero hay solución no?: ¡Claro! (bueno, hay soluciones...):
  • Lo primero que hay que hacer es obtener los frames de la cámara. O bien las obtenéis directamente a través de la clase Camera (en este post hablo de ello) o bien usáis una librería de terceros (OpenCV es una opción).
  • En segundo lugar procesáis los frames, aplicáis efectos especiales, máscaras, filtros, ... (OpenCV e ImageMagick son dos buenas opciones según lo que busquéis).
  • Y luego grabar el video (y aquí ffmpeg es "la solución").
Pues existe la librería JavaCV que integra OpenCV, ffmpeg y un montón de cosas más y además ¡os permite programar en Java!.

¿Cómo se integra?

  1. De este enlace, os descargáis javacv-*-bin.zip (* es la versión) y javacv-cppjars.zip y los descomprimimos.
  2. Creáis un proyecto (también vale uno que ya esté creado) y en la carpeta libs copiáis javacv.jar y javacpp.jar (ambos de javacv-*-bin.zip).
  3. Los archivos jar, no son más que contenedores a los que les podéis cambiar la extensión por .zip y descomprimir para ver lo que hay dentro. Pues eso tenéis que hacer con los archivos javacv-android-arm.jar, ffmpeg-*-android-arm.jar y opencv-*-android-arm.jar. Dentro encontraréis varias subcarpetas anidadas y al fondo del todo unos cuantos archivos .so (librerías nativas).
  4. Debéis crear una subcarpeta dentro de la carpeta libs en vuestro proyecto que se llame armeabi y copiáis dentro todos los .so del punto anterior (luego habrá tiempo para quitar los que no utilicéis).
  5.  En Eclipse seleccionáis el proyecto, clic derecho --> Properties --> Java Build Path --> Libraries --> Add JARs y añadís las librerías del punto 2.
  6. Dentro de la carpeta javacv-bin (del punto 1) hay una subcarpeta samples. De ella copiáis el archivo RecordActivity.java en el source de vuestro proyecto. Lo más fácil en Eclipse es copiar el archivo y pegarlo directamente en la carpeta en Eclipse, le cambiáis el nombre del paquete por el correspondiente y punto.
  7. En la cabecera del archivo anterior explica como usarlo, básicamente copiáis la parte del Manifest que indica en el vuestro (más que nada los permisos y la declaración de la clase) y luego creáis un layout para la actividad en los recursos con el código que indica a continuación.
  8. Conectáis vuestro Android (con cámara) y ejecutáis la aplicación.
Si no cambiáis nada el resultado se almacena en un archivo de extensión FLV en /mnt/sdcard/stream.flv" y el resultado es: