android ffmpeg h.264 rtsp

Descodifique el hardware de Android codificado para la alimentación de la cámara H264 usando ffmpeg en tiempo real



h.264 rtsp (2)

Estoy intentando usar el codificador H264 hardware en Android para crear video desde la cámara, y usar FFmpeg para mux en audio (todo en el propio teléfono Android)

Lo que he logrado hasta ahora es empaquetar el video H264 en paquetes rtsp y decodificarlo usando VLC (sobre UDP ), así que sé que el video está al menos correctamente formateado. Sin embargo, estoy teniendo problemas para obtener los datos de video a ffmpeg en un formato que pueda entender.

He intentado enviar los mismos paquetes rtsp a un puerto 5006 en localhost (a través de UDP), luego proporcionar a ffmpeg el archivo sdp que le indica en qué puerto local está entrando el flujo de video y cómo descifrar el video, si lo entiendo. rtsp streaming correctamente. Sin embargo, esto no funciona y tengo problemas para diagnosticar por qué, ya que ffmpeg simplemente se queda ahí esperando la entrada.

Por razones de latencia y escalabilidad, no puedo enviar el video y el audio al servidor y mezclarlos allí, tiene que hacerse en el teléfono, de la manera más liviana posible.

Lo que creo que estoy buscando son sugerencias sobre cómo se puede lograr esto. La solución óptima sería enviar el video H264 paquetes a ffmpeg través de una tubería, pero luego no puedo enviar los parámetros del archivo sdp ffmpeg que necesita para decodificar el video.

Puedo proporcionar más información a petición, como la forma en que se compila ffmpeg para Android, pero dudo que sea necesario.

Ah, y la forma en que empiezo ffmpeg es a través de la línea de comandos, realmente preferiría evitar jugar con jni si es posible.

Y la ayuda sería muy apreciada, gracias.


¿Has intentado usar java.lang.Runtime?

String[] parameters = {"ffmpeg", "other", "args"}; Program program Runtime.getRuntime().exec(parameters); InputStream in = program.getInputStream(); OutputStream out = program.getOutputStream(); InputStream err = program.getErrorStream();

Luego escribes a stdout y lees desde stdin y stderr. No es una canalización, pero debería ser mejor que usar una interfaz de red.


Un poco tarde, pero creo que esta es una buena pregunta y todavía no tiene una buena respuesta.

Si desea transmitir la cámara y el micrófono desde un dispositivo Android, tiene dos alternativas principales: implementaciones Java o NDK.

  1. Implementación de Java.

    Solo voy a mencionar la idea, pero básicamente es implementar un servidor RTSP y un protocolo RTP en java según estos estándares. Protocolo de transmisión en tiempo real versión 2.0 y formato de carga útil RTP para video H.264 . Esta tarea será muy larga y dura. Pero si está haciendo su PhP, podría ser bueno tener una buena biblioteca RTSP Java para Android.

  2. Implementación NDK.

    Esta es la alternativa incluye varias soluciones. La idea principal es utilizar una biblioteca de Power C o C ++ en nuestra aplicación de Android. Para esta instancia, FFmpeg. Esta biblioteca se puede compilar para Android y puede admitir varias arquitecturas. El problema de este enfoque es que es posible que necesite aprender sobre Android NDK, C y C ++ para lograr esto.

    Pero hay una alternativa. Puedes envolver la biblioteca c y usar FFmpeg. ¿Pero cómo?

    Por ejemplo, usar FFmpeg Android , que se ha compilado con x264, libass, fontconfig, freetype y fribidi y es compatible con varias arquitecturas. Pero todavía es difícil programar el programa. Si desea transmitir en tiempo real, necesita tratar con descriptores de archivos y flujos de entrada / salida.

    La mejor alternativa, desde el punto de vista de la programación de Java, es utilizar JavaCV . JavaCV utiliza envoltorios de bibliotecas de visión de computadora comúnmente utilizadas que incluyen: ( OpenCV , FFmpeg , etc.) y proporciona clases de utilidad para hacer que su funcionalidad sea más fácil de usar en la plataforma Java, incluido (por supuesto) Android.

    JavaCV también viene con pantalla de imagen de pantalla completa acelerada por hardware ( CanvasFrame y GLCanvasFrame ), métodos fáciles de usar para ejecutar código en paralelo en múltiples núcleos ( Parallel ), calibración geométrica y de color fácil de usar de cámaras y proyectores ( GeometricCalibrator , ProCamGeometricCalibrator , ProCamColorCalibrator ), detección y comparación de puntos de características ( ObjectFinder ), un conjunto de clases que implementan la alineación directa de la imagen de los sistemas de la cámara del proyector (principalmente GNImageAligner , ProjectiveTransformer , ProjectiveColorTransformer , ProCamTransformer , ReflectanceInitializer ), un paquete de análisis de Blobs ( Blobs ), así como funcionalidades diversas en la clase JavaCV . Algunas de estas clases también tienen una contraparte OpenCL y OpenGL, sus nombres terminan con CL o comienzan con GL , es decir: JavaCVCL , GLCanvasFrame , etc.

Pero, ¿cómo podemos usar esta solución?

Aquí tenemos una implementación básica para transmitir usando UDP.

String streamURL = "udp://ip_destination:port"; recorder = new FFmpegFrameRecorder(streamURL, frameWidth, frameHeight, 1); recorder.setInterleaved(false); // video options // recorder.setFormat("mpegts"); recorder.setVideoOption("tune", "zerolatency"); recorder.setVideoOption("preset", "ultrafast"); recorder.setVideoBitrate(5 * 1024 * 1024); recorder.setFrameRate(30); recorder.setSampleRate(AUDIO_SAMPLE_RATE); recorder.setVideoCodec(AV_CODEC_ID_H264); recorder.setAudioCodec(AV_CODEC_ID_AAC);

Esta parte del código muestra cómo inicializar el objeto FFmpegFrameRecorder llamado grabador. Este objeto capturará y codificará los marcos obtenidos de la cámara y las muestras obtenidas del micrófono.

Si desea capturar una vista previa en la misma aplicación de Android, entonces necesitamos implementar una Clase CameraPreview. Esta clase convertirá los datos en bruto que se sirven desde la cámara y creará la Vista previa y el Marco para el FFmpegFrameRecorder.

Recuerde reemplazar el ip_destination con la ip de la PC o dispositivo al que desea enviar la transmisión. El puerto puede ser 8080 como ejemplo.

@Override public Mat onCameraFrame(Mat mat) { if (audioRecordRunnable == null) { startTime = System.currentTimeMillis(); return mat; } if (recording && mat != null) { synchronized (semaphore) { try { Frame frame = converterToMat.convert(mat); long t = 1000 * (System.currentTimeMillis() - startTime); if (t > recorder.getTimestamp()) { recorder.setTimestamp(t); } recorder.record(frame); } catch (FFmpegFrameRecorder.Exception e) { LogHelper.i(TAG, e.getMessage()); e.printStackTrace(); } } } return mat; }

Este método muestra la implementación del método onCameraFrame que obtiene la Mat (imagen) de la cámara y se convierte en un fotograma y lo registra el objeto FFmpegFrameRecorder.

@Override public void onSampleReady(ShortBuffer audioData) { if (recorder == null) return; if (recording && audioData == null) return; try { long t = 1000 * (System.currentTimeMillis() - startTime); if (t > recorder.getTimestamp()) { recorder.setTimestamp(t); } LogHelper.e(TAG, "audioData: " + audioData); recorder.recordSamples(audioData); } catch (FFmpegFrameRecorder.Exception e) { LogHelper.v(TAG, e.getMessage()); e.printStackTrace(); } }

Lo mismo con el audio, audioData es un objeto ShortBuffer que será grabado por el FFmpegFrameRecorder.

En la PC o en el destino del dispositivo, puede ejecutar el siguiente comando para obtener la transmisión.

ffplay udp://ip_source:port

El ip_source es la ip del teléfono inteligente que está transmitiendo la cámara y el flujo de micrófono. El puerto debe ser el mismo 8080.

He creado una solución en mi repositorio de github aquí: UDPAVStreamer .

Buena suerte