使用live555實時播放rtsp
Android原生的MediaPlayer雖然也可以播放rtsp流媒體,可是卻有幾秒的延時,不符合實時的要求。於是需要使用第三方庫live555來解決這個問題。
1、搭建live555框架
我們先下載原始碼,然後在eclipse裡建立一個新的library工程,在工程jni下建立一個live目錄,然後將原始碼拷貝過來。之後在jni下新建一個Android.mk和Application.mk。
圖Android.mk
圖Application.mk
2、跑通rtsp client流程
分析原始碼,我們先將testRTSPClient.cpp從jni/live/testProgs裡拷貝出來,放到jni下, 然後新建一個jni_interface.h。
圖jni_interface.h
Java_com_live555_ctl_liveRtsp_start、Java_com_live555_ctl_liveRtsp_dosometing這兩個介面我們將在testRTSPClient.cpp裡實現。
在Java_com_live555_ctl_liveRtsp_start裡我們根據傳過來的url和progName呼叫openURL,然後調起主迴圈doEventLoop。 這個時候,我們可以跑通rtsp client的流程,可以連線server了,可是,接收到的視訊資料還沒有做處理。
3、接收處理視訊資料
在live555的架構裡,要說明一下Sink source,這兩者的概念及關係:
liveMedia庫中Sink就是消費資料的物件,比如把接收到的資料儲存到檔案,這個檔案就是一個Sink。 Source就是生產資料的物件,比如通過 RTP讀取資料。資料流經過多個source和sinks。
圖MediaSink
圖MediaSource
根據rtsp流程,我們在continueAfterSETUP裡找到了
{
scs.subsession->sink =DummySink::createNew(env, *scs.subsession, rtspClient->url());
}
將其替換為
{
bool oneFilePerFrame = false;
char const* fmtp_spropparametersets =(*scs.subsession).fmtp_spropparametersets();
scs.subsession->sink =H264VideoFileSink::createNew(env, (charconst*)"/storage/sdcard0/testRtsp.mp4",fmtp_spropparametersets,DUMMY_SINK_RECEIVE_BUFFER_SIZE,oneFilePerFrame);
}
替換DummySink為H264VideoFileSink後,我們就可以發現,rtsp傳過來的視訊資料被儲存在了/storage/sdcard0/testRtsp.mp4。
我們將H264VideoStreamSink.cpp、H264VideoStreamSink.hh、 H264or5VideoStreamSink.cpp、H264or5VideoStreamSink.hh、 StreamSink.cpp、StreamSink.hh拷貝到jni下,改名為WrH264VideoStreamSink.cpp、WrH264VideoStreamSink.hh、WrH264or5VideoStreamSink.cpp、WrH264or5VideoStreamSink.hh、 WrStreamSink.cpp、 WrStreamSink.hh。
{
scs.subsession->sink =H264VideoFileSink::createNew(env,(charconst*)"/storage/sdcard0/testRtsp.mp4",fmtp_spropparametersets,DUMMY_SINK_RECEIVE_BUFFER_SIZE,oneFilePerFrame);
}
改為
{
scs.subsession->sink =WrH264VideoStreamSink::createNew(env,
&function_callback,fmtp_spropparametersets,
DUMMY_SINK_RECEIVE_BUFFER_SIZE);
}
傳遞一個回撥進去typedefvoidfunctioncallback(unsignedchar* buffer,int datasize,int videowidth,int videoheight, int pts),然後修改相應的程式碼,將儲存為mp4的功能去掉,改為將h264的視訊frame通過回撥傳出來。
{
void function_callback(unsigned char* buffer,int datasize, int videowidth, int videoheight, int pts)
{
if(0!= liveRtsp_env && 0 != liveRtsp_obj && 0 != liveRtsp_array) {
liveRtsp_env->SetByteArrayRegion(liveRtsp_array,0, datasize, (const jbyte*)buffer);
int iframeSize = datasize;
liveRtsp_env->CallVoidMethod(*liveRtsp_obj,mid_onNativeCallback, liveRtsp_array, videowidth, videoheight, pts, datasize);
}
}
}
然後通過mid_onNativeCallback =env->GetMethodID(clsstring, "onNativeCallback","([BIIII)V")將h264的frame傳到Java層。
4、Java層介面
圖 live555介面
至此,live555 rtsp client的library就搭建好了。
5、應用live555rtsp client的library
{
String prog = "dlna_connect";
byte[] progName =new byte[prog.length() +1];
System.arraycopy(prog.getBytes(), 0, progName, 0, prog.length());
progName[prog.length()] = 0;
Stringuri = playURI;//"rtsp://192.168.43.1:8086";
byte[] nameUri =new byte[uri.length() +1];
System.arraycopy(uri.getBytes(), 0, nameUri, 0, uri.length());
nameUri[uri.length()] = 0;
if(null == mliveRtsp)
mliveRtsp =new liveRtsp();
mliveRtsp.mliveRtspInterface = liveRtspIf;
mliveRtsp.start(progName, nameUri);
}
這裡開始啟動liveRtsp
{
public liveRtsp.liveRtspInterface liveRtspIf = newliveRtsp.liveRtspInterface() {
@Override
public voidliveRtspCallback(byte[] data, int width, int height,
intpts, int len) {
...在這裡可以收到rtsp的視訊幀
}
}
}
接收到視訊幀後,需要解碼顯示到Surface上,需要注意的是,live555裡只有一個主執行緒,所以回撥也是在liveRtsp的主執行緒裡,即建立liveRtsp的執行緒。