1. 程式人生 > >使用live555實時播放rtsp

使用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的執行緒。