libstreaming是一個 API,只需幾行程式碼,您就可以使用 RTP over UDP 串流 Android 裝置的攝影機和/或麥克風。
要啟動與某個對等點的串流會話,您需要實現的第一步稱為「發送訊號」。在此步驟中,您將聯絡接收者並發送傳入流的描述。您可以透過三種方式使用 libstreaming 來做到這一點。
API 的完整 javadoc 文件可在此處取得:http://guigui.us/libstreaming/doc
Android 上可以透過三種方式從外設取得編碼資料:
MediaRecorder API 不適用於串流媒體應用程序,但可用於從手機週邊設備檢索編碼資料。技巧是將 MediaRecorder 實例配置為寫入LocalSocket而不是常規檔案(請參閱MediaStream.java )。
編輯:從 Android Lollipop 開始,出於安全原因,不再可能使用LocalSocket 。但使用ParcelFileDescriptor就可以解決問題。更多詳細資訊請參閱MediaStream.java檔案! (感謝那些人的見解)
這個黑客有一些限制:
很難說這個駭客技術在手機上的效果如何。但它在許多設備上運作良好。
MediaCodec API 沒有我剛才提到的限制,但有它自己的問題。實際上有兩種使用 MediaCodec API 的方法:使用緩衝區或使用表面。
buffer-to-buffer方法使用對dequeueInputBuffer和queueInputBuffer的呼叫來向編碼器提供原始資料。這看起來很容易吧?事實並非如此,因為您透過此 API 存取的視訊編碼器使用不同的顏色格式,您需要支援所有這些格式。此處提供了這些顏色格式的清單。此外,許多編碼器聲稱支援顏色格式,但它們實際上並未正確支持,或者可能會出現小故障。
所有硬體包都致力於解決這些問題。特別參見EncoderDebugger類別。
如果使用該 API 進行串流失敗,libstreaming 將使用MediaRecorder API進行串流傳輸。
表面到緩衝區方法使用 createInputSurface() 方法。此方法可能是對來自相機的原始影片進行編碼的最佳方法,但它需要 Android 4.3 及更高版本。
gl包專用於將 MediaCodec API 與表面結合使用。
預設情況下,libstreaming 中尚未啟用它,但您可以使用setStreamingMethod(byte)方法強制啟用它。
一旦來自周邊的原始資料被編碼,它就會被封裝在適當的 RTP 流中。必須使用的分組演算法取決於資料的格式(H.264、H.263、AMR 和 AAC),並且均在各自的 RFC 中指定:
如果您正在尋找上述 RFC 之一的基本實現,請檢查對應類別的原始程式碼。
從 libstreaming 2.0 版本開始,RTCP 封包也會傳送到接收方。僅實施寄件者報告。它們實際上是口型同步所需要的。
rtp包處理 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 " />
這個例子是從這個簡單的 Android 應用程式中提取的。這可以是活動、片段或服務的一部分。
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只是方便了Session物件的創建。視訊串流需要呼叫setSurfaceView ,這並不奇怪,因為 Android 需要一個有效的表面來錄製視訊(這是MediaRecorder API 的惱人的限制)。在 Android 4.3 上,可以不使用SurfaceView進行串流傳輸,但尚未實現。對setContext(Context) 的呼叫是必要的,它允許H264Stream物件和AACStream物件使用SharedPreferences來儲存和復原資料。
Session物件代表某個對等方的流會話。它包含一個或多個Stream對象,這些物件在呼叫start() (或stop() )方法時啟動(或停止)。
getSessionDescription()方法將以字串的形式傳回會話的 SDP。在呼叫之前,必須確保Session已經配置好。在 Session 實例上呼叫configure()或startPreview()後,將呼叫回呼onSessionConfigured() 。
在上面的範例中,Session 實例以非同步方式使用,並且對其方法的呼叫不會阻塞。當回呼被呼叫時你就知道事情什麼時候完成了。
您也可以以同步方式使用 Session 對象,如下所示:
// 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 ();
查看 wiki 的此頁面和範例 3。
< service android : name = " net.majorkernelpanic.streaming.rtsp.RtspServer " />
如果您決定覆蓋RtspServer,請相應地更改上面的行。
Editor editor = PreferenceManager . getDefaultSharedPreferences ( this ). edit ();
editor . putString ( RtspServer . KEY_PORT , String . valueOf ( 1234 ));
editor . commit ();
該連接埠確實作為字串儲存在首選項中,這是有充分理由的。 EditTextPreference 物件將其輸入保存為字串,並且不能輕鬆(需要覆蓋它)配置為將其儲存為整數。
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 ));
請造訪此 github 頁面,以了解如何使用此流堆疊及其執行情況。