2011-10-12 31 views
28

Estoy tratando de utilizar el codificador de hardware H264 en Android para crear vídeo de la cámara, y el uso de FFmpeg al mux en el audio (todo en el propio teléfono Android)Decode androide usando ffmpeg en tiempo real

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

He intentado el envío de los mismos rtsp paquetes a un puerto 5006 en el servidor local (a través de UDP), a continuación, proporcionando ffmpeg con el archivo sdp que le dice qué puerto local de la secuencia de vídeo está entrando en y cómo decodificar el video , si entiendo rtsp transmitiendo correctamente. Sin embargo, esto no funciona y tengo problemas para diagnosticar por qué, ya que ffmpeg está ahí esperando la entrada.

Por razones de latencia y escalabilidad, no puedo simplemente enviar el video y el audio al servidor y mux allí, tiene que ser hecho en el teléfono, de la manera más ligera 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 empaquetado H264 a ffmpeg a través de una tubería, pero luego no puedo enviar ffmpeg los parámetros del archivo sdp que necesita para decodificar el video.

Puedo proporcionar más información bajo petición, como cómo se compila ffmpeg para Android, pero dudo que sea necesario.

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

Y la ayuda sería muy apreciada, gracias.

+3

¿por qué está decodificando usando ffmpeg? use el objeto MediaPlayer incorporado –

+0

¿Ha intentado usar live555 para transmitir la salida de ffmpeg a través de RTSP? Además, ¿no debería ffmpeg sondear la transmisión y descubrir la información del flujo en sí? – Shark

+0

Creo que Aviad tiene la verdad. ¿Cómo sabes siquiera qué formato de video produce la cámara? –

Respuesta

1

¿Has probado a 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 escribe en stdout y lee en stdin y stderr. No es un conducto, pero debería ser mejor que usar una interfaz de red.

+0

OP aquí, ya no estoy trabajando en este problema (me di por vencido), pero debería agregar que intenté exactamente esto. No recuerdo todos los detalles, pero traté de usar tanto el InputStream como varias tuberías FIFO usando el SO (One pipe para el video, uno para el audio). Sin embargo, el problema con este método fue que no pude proporcionar suficiente información para comprender y decodificar los paquetes de video generados por la alimentación de la cámara. La verdadera razón por la que quería usar rtsp era porque, teóricamente, proporcionaría suficiente información de FFmpeg para decodificar la transmisión en vivo. – joebobfrank

1

Un poco tarde pero creo que esta es una buena pregunta y aún 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 mencionaré la idea, pero básicamente es implementar un servidor RTSP y un protocolo RTP en Java basados ​​en estos estándares Real-Time Streaming Protocol Version 2.0 y RTP Payload Format for H.264 Video. Esta tarea será muy larga y difícil. Pero si estás haciendo tu PhP, sería bueno tener una buena lib de RTSP Java para Android.

  2. Implementación NDK.

    Esta es una alternativa que incluye varias soluciones. La idea principal es utilizar una biblioteca de poder C o C++ en nuestra aplicación 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. Puede envolver la biblioteca c y usar el FFmpeg. ¿Pero cómo?

    Por ejemplo, usando FFmpeg Android, que se ha compilado con x264, libass, fontconfig, freetype y fribidi y admite varias arquitecturas. Pero aún es difícil programar el flujo de datos en tiempo real si desea transmitir en tiempo real los descriptores de archivos y las transmisiones entrantes/salientes.

    La mejor alternativa, desde el punto de vista de la programación de Java, es usar JavaCV. JavaCV utiliza contenedores de bibliotecas de uso común de la visión por ordenador que incluye: (OpenCV, FFmpeg, etc, y ofrece clases de utilidad para hacer su funcionalidad más fácil de usar en la plataforma Java, incluyendo (por supuesto) Android

    JavaCV también viene. con pantalla de visualización 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, , ProCamColorCalibrator) , detección y coincidencia de puntos característicos (ObjectFinder), un conjunto de clases que implementa alineación directa de imágenes de los sistemas de la cámara del proyector (principalmente GNImageAligner, ProjectiveTransformer, ProjectiveColorTransformer, ProCamTransformer y ReflectanceInitializer), un paquete de análisis de blobs (Blobs), así como varias funciones en la clase JavaCV. Algunas de estas clases también tienen una contraparte OpenCL y OpenGL, sus nombres que terminan con CL o que comienzan con GL, es decir .: JavaCVCL, GLCanvasFrame, etc.

Pero ¿cómo podemos utilizar 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 llamada grabadora. Este objeto capturará y codificará los fotogramas 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 tenemos que implementar una clase CameraPreview esta clase convertirá los datos brutos servidos 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 donde 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 obtener el Mat (imagen) de la cámara y se convierte como un marco y registrados por 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 del audioData es un objeto ShortBuffer que será por el grabador FFmpegFrameRecorder.

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

ffplay udp://ip_source:port 

El ip_source es la dirección IP del teléfono inteligente que se está transmitiendo la cámara y el micrófono corriente. El puerto debe ser el mismo 8080.

Creé una solución en mi repositorio github aquí: UDPAVStreamer.

Buena suerte