整合csipsimple

最近公司案子要用到sip(原本搞WebRTC了三個月才又改成這個)自己架了signalling server之後又說要用pjsua當libarary做,一看下去才知道pjsua現在Android平台上根本還不支援影像

搜尋了一下發現csipsimple根本就是終極神器:一個人自幹了四年,功能多到爆:

  • 支援多國sip服務(根本都沒聽過)
  • 多國語言
  • 整合Android內容撥號功能,甚至可以加入系統聯絡人跟撥號紀錄!
  • 整合pjsua, webrtc (from google)以及多種影音codec
  • 重點是現在已經有影像功能的測試版apk!

不是它還有誰可以當我的救贖呢?

一切都從SipService開始,它啟動後會開始監聽裝置狀態跟Sip帳號的更動,讓它可以在使用者設定完成後就開始依序的相關動作。Register receiver有兩個 method,其中一個是監視 service的狀態。

要整合 csipsimple到自己的專案,你需要做的是:

  • 複製所有 so檔到jniLibs裡(略過重新編譯 jni裡所有C/C++原始碼的麻煩)
  • 複製 com.csipsimple下除了 com.csipsimple.ui 之外的所有 package(因為我只用到 Basic wizard所以只複製了相關的物件,其他類型的 wizard不用理)
  • 將 csipsimple AndroidManifest.xml 裡宣告的 Service, Receiver 複製到 自己的 AndroidManifest.xml。

    先(最好)不要修改 csipsimple 原本的 package名稱,因為 csipsimple 內部元件互動幾乎都是靠 定義在SipConfigManager裡面的intent action,若更動可能得花很多時間測試才知道正確的組合!我在自己專案的 start up activity裡要啟動 SipService 就卡關很久!才發現它一直隻找不到要啟動的 Service。

  • 另外就是啟動 Service的 intent.setPackage()可以註解掉。

    Android 5.0之後不允許implicit intent,所以又會碰到無法找到SipService的狀況。解法可參考下面這段code(忘記在哪找到的..)

/***
     * Android L (lollipop, API 21) introduced a new problem when trying to invoke implicit intent,
     * "java.lang.IllegalArgumentException: Service Intent must be explicit"
     *
     * If you are using an implicit intent, and know only 1 target would answer this intent,This method will help you turn the implicit intent into the explicit form.
     *
     * Inspired from SO answer: http://stackoverflow.com/a/26318757/1446466
     * @param context
     * @param implicitIntent - The original implicit intent
     * @return Explicit Intent created from the implicit original intent
     */
    public static Intent createExplicitFromImplicitIntent(Context context, Intent implicitIntent) {
        // Retrieve all services that can match the given intent
        PackageManager pm = context.getPackageManager();
        List<ResolveInfo> resolveInfo = pm.queryIntentServices(implicitIntent, 0);

        // Make sure only one match was found
        if (resolveInfo == null || resolveInfo.size() != 1) {
            return null;
        }

        // Get component info and create ComponentName
        ResolveInfo serviceInfo = resolveInfo.get(0);
        String packageName = serviceInfo.serviceInfo.packageName;
        String className = serviceInfo.serviceInfo.name;
        ComponentName component = new ComponentName(packageName, className);

        // Create a new intent. Use the old one for extras and such reuse
        Intent explicitIntent = new Intent(implicitIntent);

        // Set the component to be explicit
        explicitIntent.setComponent(component);

        return explicitIntent;
    }
  • 參考 #SipHome 的 startSipService()
    // Check if returned compName is null
    ComponentName compName = startService(intent);
    
  • AIDL檔的位置記得在 build.gradle 裡特別指定,因為 csipsimple 所使用的 AIDL檔放在 com.csipsimple.api 裡,而不是 Android Studio 預設的 /java/main/aidl。在build.gradle裡指定AIDL路徑的語法是:
    android {
      compileSdkVersion 21
      buildToolsVersion "21.1.0"
      ...
      sourceSets {
          main {
              aidl.srcDirs = ['src/main/java']
          }
      }
    }
    
  • 新增一筆 Sip 帳號

    用 BasePrefsWizard 來新增一個 Basic 帳號(帳號密碼,伺服器url,給一個帳號別名)

  • 播出電話

    參考 DialerFragment 的 placeCall(); 裡面有如何從 fragment 連上 Service 的做法:bindService(); 下面是我自己改的撥出語音電話跟影像電話 ```language-java private void placeAudioCall() { dialFeedback.giveFeedback(3001); placeCallWithOption(null); }

private void placeVideoCall() { dialFeedback.giveFeedback(3001); Bundle b = new Bundle(); b.putBoolean(SipCallSession.OPT_CALL_VIDEO, true); placeCallWithOption(b); } ```

本篇文章未完成,更新會持續補上。