libstreaming es una API que le permite, con solo unas pocas líneas de código, transmitir la cámara y/o el micrófono de un dispositivo con Android usando RTP sobre UDP.
El primer paso que deberá realizar para iniciar una sesión de transmisión con algún par se llama "señalización". Durante este paso, se comunicará con el receptor y le enviará una descripción de las transmisiones entrantes. Tienes tres formas de hacerlo con libstreaming.
La documentación javadoc completa de la API está disponible aquí: http://guigui.us/libstreaming/doc
Hay tres formas en Android de obtener datos codificados de los periféricos:
La API MediaRecorder no fue diseñada para aplicaciones de transmisión, pero puede usarse para recuperar datos codificados de los periféricos del teléfono. El truco consiste en configurar una instancia de MediaRecorder para escribir en un LocalSocket en lugar de en un archivo normal (consulte MediaStream.java ).
Editar: a partir de Android Lollipop, usar un LocalSocket ya no es posible por razones de seguridad. Pero usar un ParcelFileDescriptor es suficiente. ¡Más detalles en el archivo MediaStream.java ! (Gracias a esos chicos por la información)
Este truco tiene algunas limitaciones:
Es difícil saber qué tan bien funcionará este truco en un teléfono. Sin embargo, funciona bien en muchos dispositivos.
La API MediaCodec no presenta las limitaciones que acabo de mencionar, pero tiene sus propios problemas. En realidad, hay dos formas de utilizar la API MediaCodec: con buffers o con una superficie.
El método de búfer a búfer utiliza llamadas a dequeueInputBuffer y [ queueInputBuffer ](http://developer.android.com/reference/android/media/MediaCodec.html#queueInputBuffer(int, int, int, long, int)) para alimente el codificador con datos sin procesar. Eso parece fácil ¿verdad? Bueno, no lo es, porque los codificadores de video a los que tienes acceso con esta API usan diferentes formatos de color y debes admitirlos todos. Una lista de esos formatos de color está disponible aquí. Además, muchos codificadores afirman ser compatibles con formatos de color que en realidad no admiten correctamente o que pueden presentar pequeños fallos.
Todo el paquete hw está dedicado a resolver esos problemas. Véase en particular la clase EncoderDebugger .
Si la transmisión con esa API falla, libstreaming recurre a la transmisión con la API MediaRecorder .
El método de superficie a búfer utiliza el método createInputSurface(). Este método es probablemente la mejor manera de codificar video sin procesar desde la cámara, pero requiere Android 4.3 y superior.
El paquete gl está dedicado a utilizar la API MediaCodec con una superficie.
Aún no está habilitado de forma predeterminada en libstreaming, pero puede forzarlo con el método setStreamingMethod(byte) .
Una vez que se han codificado los datos sin procesar de los periféricos, se encapsulan en un flujo RTP adecuado. El algoritmo de paquetización que se debe utilizar depende del formato de los datos (H.264, H.263, AMR y AAC) y están todos especificados en su respectivo RFC:
Si está buscando una implementación básica de uno de los RFC mencionados anteriormente, consulte las fuentes de la clase correspondiente.
Los paquetes RTCP también se envían al receptor desde la versión 2.0 de libstreaming. Solo se implementan los informes del remitente. De hecho, son necesarios para la sincronización de labios.
El paquete rtp maneja la paquetización de datos codificados en paquetes RTP.
< uses-permission android : name = " android.permission.INTERNET " />
< uses-permission android : name = " android.permission.WRITE_EXTERNAL_STORAGE " />
< uses-permission android : name = " android.permission.RECORD_AUDIO " />
< uses-permission android : name = " android.permission.CAMERA " />
Este ejemplo está extraído de esta sencilla aplicación para Android. Esto podría ser parte de una Actividad, un Fragmento o un Servicio.
protected void onCreate ( Bundle savedInstanceState ) {
...
mSession = SessionBuilder . getInstance ()
. setCallback ( this )
. setSurfaceView ( mSurfaceView )
. setPreviewOrientation ( 90 )
. setContext ( getApplicationContext ())
. setAudioEncoder ( SessionBuilder . AUDIO_NONE )
. setAudioQuality ( new AudioQuality ( 16000 , 32000 ))
. setVideoEncoder ( SessionBuilder . VIDEO_H264 )
. setVideoQuality ( new VideoQuality ( 320 , 240 , 20 , 500000 ))
. build ();
mSurfaceView . getHolder (). addCallback ( this );
...
}
public void onPreviewStarted () {
Log . d ( TAG , "Preview started." );
}
@ Override
public void onSessionConfigured () {
Log . d ( TAG , "Preview configured." );
// Once the stream is configured, you can get a SDP formated session description
// that you can send to the receiver of the stream.
// For example, to receive the stream in VLC, store the session description in a .sdp file
// and open it with VLC while streming.
Log . d ( TAG , mSession . getSessionDescription ());
mSession . start ();
}
@ Override
public void onSessionStarted () {
Log . d ( TAG , "Streaming session started." );
...
}
@ Override
public void onSessionStopped () {
Log . d ( TAG , "Streaming session stopped." );
...
}
@ Override
public void onBitrateUpdate ( long bitrate ) {
// Informs you of the bandwidth consumption of the streams
Log . d ( TAG , "Bitrate: " + bitrate );
}
@ Override
public void onSessionError ( int message , int streamType , Exception e ) {
// Might happen if the streaming at the requested resolution is not supported
// or if the preview surface is not ready...
// Check the Session class for a list of the possible errors.
Log . e ( TAG , "An error occured" , e );
}
@ Override
public void surfaceChanged ( SurfaceHolder holder , int format , int width ,
int height ) {
}
@ Override
public void surfaceCreated ( SurfaceHolder holder ) {
// Starts the preview of the Camera
mSession . startPreview ();
}
@ Override
public void surfaceDestroyed ( SurfaceHolder holder ) {
// Stops the streaming session
mSession . stop ();
}
SessionBuilder simplemente facilita la creación de objetos de sesión . La llamada a setSurfaceView es necesaria para la transmisión de video, lo que no debería sorprender ya que Android requiere una superficie válida para grabar video (es una limitación molesta de la API MediaRecorder ). En Android 4.3, la transmisión sin SurfaceView es posible, pero aún no está implementada. La llamada a setContext(Context) es necesaria, permite que los objetos H264Stream y AACStream almacenen y recuperen datos usando SharedPreferences .
Un objeto Session representa una sesión de streaming para algún par. Contiene uno o más objetos Stream que se inician (o se detienen) cuando se invoca el método start() (o stop() ).
El método getSessionDescription() devolverá un SDP de la sesión en forma de cadena. Antes de llamarlo debes asegurarte de que la Sesión ha sido configurada. Después de llamar a configure() o startPreview() en su instancia de sesión, se llamará la devolución de llamada onSessionConfigured() .
En el ejemplo presentado anteriormente, la instancia de sesión se utiliza de forma asincrónica y las llamadas a sus métodos no se bloquean. Sabes cuándo están hechas las cosas cuando se llaman devoluciones de llamada.
También puedes usar un objeto Session de forma sincrónica así:
// Blocks until the all streams are configured
try {
mSession . syncConfigure ();
} catch ( Exception e ) {
...
}
Strinf sdp = mSession . getSessionDescription ();
...
// Blocks until streaming actually starts.
try {
mSession . syncStart ();
} catch ( Exception e ) {
...
}
...
mSession . syncStop ();
Consulte esta página de la wiki y el ejemplo 3.
< service android : name = " net.majorkernelpanic.streaming.rtsp.RtspServer " />
Si decide anular RtspServer, cambie la línea anterior en consecuencia.
Editor editor = PreferenceManager . getDefaultSharedPreferences ( this ). edit ();
editor . putString ( RtspServer . KEY_PORT , String . valueOf ( 1234 ));
editor . commit ();
De hecho, el puerto se almacena como una cadena en las preferencias, hay una buena razón para ello. El objeto EditTextPreference guarda su entrada como una cadena y no se puede configurar fácilmente (habría que anularlo) para almacenarla como un número entero.
SessionBuilder . getInstance ()
. setSurfaceHolder ( mSurfaceView . getHolder ())
. setContext ( getApplicationContext ())
. setAudioEncoder ( SessionBuilder . AUDIO_AAC )
. setVideoEncoder ( SessionBuilder . VIDEO_H264 );
// Starts the RTSP server
context . startService ( new Intent ( this , RtspServer . class ));
// Stops the RTSP server
context . stopService ( new Intent ( this , RtspServer . class ));
Visite esta página de github para ver cómo se puede utilizar esta pila de transmisión y cómo funciona.