libstreaming est une API qui vous permet, avec seulement quelques lignes de code, de diffuser la caméra et/ou le microphone d'un appareil Android en utilisant RTP sur UDP.
La première étape que vous devrez réaliser pour démarrer une session de streaming vers un homologue est appelée « signalisation ». Au cours de cette étape, vous contacterez le destinataire et lui enverrez une description des flux entrants. Vous avez trois façons de le faire avec libstreaming.
La documentation javadoc complète de l'API est disponible ici : http://guigui.us/libstreaming/doc
Il existe trois manières sur Android d'obtenir des données encodées depuis les périphériques :
L'API MediaRecorder n'est pas destinée aux applications de streaming mais peut être utilisée pour récupérer des données encodées depuis les périphériques du téléphone. L'astuce consiste à configurer une instance MediaRecorder pour écrire dans un LocalSocket au lieu d'un fichier normal (voir MediaStream.java ).
Edit : depuis Android Lollipop, l'utilisation d'un LocalSocket n'est plus possible pour des raisons de sécurité. Mais utiliser un ParcelFileDescriptor fait l'affaire. Plus de détails dans le fichier MediaStream.java ! (Merci à ces gars pour la perspicacité)
Ce hack a quelques limites :
Il est difficile de dire dans quelle mesure ce hack fonctionnera sur un téléphone. Cela fonctionne cependant bien sur de nombreux appareils.
L'API MediaCodec ne présente pas les limitations que je viens de mentionner, mais a ses propres problèmes. Il existe en fait deux manières d'utiliser l'API MediaCodec : avec des buffers ou avec une surface.
La méthode buffer-to-buffer utilise des appels à dequeueInputBuffer et [ queueInputBuffer ](http://developer.android.com/reference/android/media/MediaCodec.html#queueInputBuffer(int, int, int, long, int)) pour alimenter l’encodeur avec des données brutes. Cela semble facile, non ? Eh bien, ce n'est pas le cas, car les encodeurs vidéo auxquels vous avez accès avec cette API utilisent différents formats de couleurs et vous devez tous les prendre en charge. Une liste de ces formats de couleurs est disponible ici. De plus, de nombreux encodeurs prétendent prendre en charge des formats de couleurs qu'ils ne prennent pas réellement en charge correctement ou qui peuvent présenter de petits problèmes.
Tout le package hw est dédié à la résolution de ces problèmes. Voir en particulier la classe EncoderDebugger .
Si le streaming avec cette API échoue, libstreaming utilise le streaming avec l' API MediaRecorder .
La méthode surface à tampon utilise la méthode createInputSurface(). Cette méthode est probablement le meilleur moyen d'encoder une vidéo brute à partir de la caméra, mais elle nécessite Android 4.3 et supérieur.
Le package gl est dédié à l'utilisation de l'API MediaCodec avec une surface.
Il n'est pas encore activé par défaut dans libstreaming mais vous pouvez le forcer avec la méthode setStreamingMethod(byte) .
Une fois les données brutes des périphériques codées, elles sont encapsulées dans un flux RTP approprié. L'algorithme de mise en paquets qui doit être utilisé dépend du format des données (H.264, H.263, AMR et AAC) et sont tous spécifiés dans leur RFC respectif :
Si vous recherchez une implémentation basique de l'une des RFC mentionnées ci-dessus, vérifiez les sources de la classe correspondante.
Les paquets RTCP sont également envoyés au récepteur depuis la version 2.0 de libstreaming. Seuls les rapports de l'expéditeur sont implémentés. Ils sont en fait nécessaires à la synchronisation labiale.
Le package rtp gère la mise en paquets des données codées dans les paquets 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 " />
Cet exemple est extrait de cette simple application Android. Cela peut faire partie d'une activité, d'un fragment ou d'un service.
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 ();
}
Le SessionBuilder facilite simplement la création d'objets Session . L'appel à setSurfaceView est nécessaire pour le streaming vidéo, ce qui ne devrait pas surprendre puisque Android nécessite une surface valide pour l'enregistrement vidéo (c'est une limitation ennuyeuse de l'API MediaRecorder ). Sur Android 4.3, le streaming sans SurfaceView est possible mais pas encore implémenté. L'appel à setContext(Context) est nécessaire, il permet aux objets H264Stream et AACStream de stocker et récupérer des données à l'aide de SharedPreferences .
Un objet Session représente une session de streaming vers un homologue. Il contient un ou plusieurs objets Stream qui sont démarrés (resp. arrêtés) lorsque la méthode start() (resp. stop() ) est invoquée.
La méthode getSessionDescription() renverra un SDP de la session sous la forme d'une String. Avant de l'appeler, vous devez vous assurer que la Session a été configurée. Après avoir appelé configure() ou startPreview() sur votre instance de session, le rappel onSessionConfigured() sera appelé.
Dans l'exemple présenté ci-dessus, l'instance Session est utilisée de manière asynchrone et les appels à ses méthodes ne bloquent pas. Vous savez quand les choses sont terminées lorsque des rappels sont appelés.
Vous pouvez également utiliser un objet Session de manière synchrone comme ceci :
// 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 ();
Consultez cette page du wiki et l'exemple 3.
< service android : name = " net.majorkernelpanic.streaming.rtsp.RtspServer " />
Si vous décidez de remplacer RtspServer, modifiez la ligne ci-dessus en conséquence.
Editor editor = PreferenceManager . getDefaultSharedPreferences ( this ). edit ();
editor . putString ( RtspServer . KEY_PORT , String . valueOf ( 1234 ));
editor . commit ();
Le port est en effet stocké sous forme de String dans les préférences, il y a une bonne raison à cela. L'objet EditTextPreference enregistre son entrée sous forme de chaîne et ne peut pas facilement (il faudrait le remplacer) être configuré pour la stocker sous forme d'entier.
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 ));
Visitez cette page github pour voir comment cette pile de streaming peut être utilisée et comment elle fonctionne.