libstreaming é uma API que permite, com apenas algumas linhas de código, transmitir a câmera e/ou microfone de um dispositivo Android usando RTP sobre UDP.
O primeiro passo que você precisa realizar para iniciar uma sessão de streaming para algum peer é chamado de 'sinalização'. Durante esta etapa você entrará em contato com o destinatário e enviará uma descrição dos fluxos recebidos. Você tem três maneiras de fazer isso com libstreaming.
A documentação javadoc completa da API está disponível aqui: http://guigui.us/libstreaming/doc
Existem três maneiras no Android de obter dados codificados dos periféricos:
A API MediaRecorder não foi projetada para aplicativos de streaming, mas pode ser usada para recuperar dados codificados dos periféricos do telefone. O truque é configurar uma instância MediaRecorder para gravar em um LocalSocket em vez de em um arquivo normal (consulte MediaStream.java ).
Editar: a partir do Android Lollipop, usar um LocalSocket não é mais possível por motivos de segurança. Mas usar um ParcelFileDescriptor resolve. Mais detalhes no arquivo MediaStream.java ! (Obrigado a esses caras pelo insight)
Este hack tem algumas limitações:
É difícil dizer até que ponto esse hack funcionará bem em um telefone. No entanto, funciona bem em muitos dispositivos.
A API MediaCodec não apresenta as limitações que acabei de mencionar, mas tem seus próprios problemas. Na verdade, existem duas maneiras de usar a API MediaCodec: com buffers ou com superfície.
O método buffer para buffer usa chamadas para dequeueInputBuffer e [ queueInputBuffer ](http://developer.android.com/reference/android/media/MediaCodec.html#queueInputBuffer(int, int, int, long, int)) para alimentar o codificador com dados brutos. Isso parece fácil, certo? Bem, não é, porque os codificadores de vídeo aos quais você tem acesso com esta API usam formatos de cores diferentes e você precisa oferecer suporte a todos eles. Uma lista desses formatos de cores está disponível aqui. Além disso, muitos codificadores afirmam ter suporte para formatos de cores que na verdade não suportam adequadamente ou podem apresentar pequenas falhas.
Todo o pacote hw é dedicado a resolver esses problemas. Veja em particular a classe EncoderDebugger .
Se o streaming com essa API falhar, o libstreaming retornará ao streaming com a API MediaRecorder .
O método superfície para buffer usa o método createInputSurface(). Este método é provavelmente a melhor maneira de codificar vídeo bruto da câmera, mas requer Android 4.3 e superior.
O pacote gl é dedicado ao uso da API MediaCodec com uma superfície.
Ainda não está habilitado por padrão em libstreaming, mas você pode forçá-lo com o método setStreamingMethod(byte) .
Depois que os dados brutos dos periféricos forem codificados, eles serão encapsulados em um fluxo RTP adequado. O algoritmo de empacotamento que deve ser utilizado depende do formato dos dados (H.264, H.263, AMR e AAC) e estão todos especificados em suas respectivas RFC:
Se você está procurando uma implementação básica de uma das RFC mencionadas acima, verifique as fontes da classe correspondente.
Pacotes RTCP também são enviados ao receptor desde a versão 2.0 do libstreaming. Somente Relatórios do Remetente são implementados. Na verdade, eles são necessários para sincronização labial.
O pacote rtp lida com o empacotamento de dados codificados em pacotes 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 exemplo foi extraído deste aplicativo Android simples. Isso pode ser parte de uma atividade, fragmento ou serviço.
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 ();
}
O SessionBuilder simplesmente facilita a criação de objetos Session . A chamada para setSurfaceView é necessária para streaming de vídeo, o que não deve ser uma surpresa, já que o Android requer uma superfície válida para gravação de vídeo (é uma limitação irritante da API MediaRecorder ). No Android 4.3, o streaming sem SurfaceView é possível, mas ainda não foi implementado. A chamada para setContext(Context) é necessária, pois permite que objetos H264Stream e objetos AACStream armazenem e recuperem dados usando SharedPreferences .
Um objeto Session representa uma sessão de streaming para algum peer. Ele contém um ou mais objetos Stream que são iniciados (resp. parados) quando o método start() (resp. stop() ) é invocado.
O método getSessionDescription() retornará um SDP da sessão na forma de uma String. Antes de chamá-lo, você deve se certificar de que a Sessão foi configurada. Após chamar configure() ou startPreview() em sua instância de Session, o retorno de chamada onSessionConfigured() será chamado.
No exemplo apresentado acima, a instância Session é utilizada de forma assíncrona e as chamadas aos seus métodos não são bloqueadas. Você sabe quando as coisas estão prontas quando os retornos de chamada são chamados.
Você também pode usar um objeto Session de maneira síncrona assim:
// 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 ();
Confira esta página do wiki e o exemplo 3.
< service android : name = " net.majorkernelpanic.streaming.rtsp.RtspServer " />
Se você decidir substituir o RtspServer, altere a linha acima de acordo.
Editor editor = PreferenceManager . getDefaultSharedPreferences ( this ). edit ();
editor . putString ( RtspServer . KEY_PORT , String . valueOf ( 1234 ));
editor . commit ();
A porta é de fato armazenada como uma String nas preferências, há uma boa razão para isso. O objeto EditTextPreference salva sua entrada como uma String e não pode ser facilmente configurado (seria necessário substituí-lo) para armazená-lo como um número inteiro.
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 do github para ver como essa pilha de streaming pode ser usada e como ela funciona.