linphone錄音分析


linphone自feature/record_audio_and_send_it_through_chat版本後對代碼結構做了比較大的更改,在AndroidStudio上無法編譯,因此嘗試將新版linphone的acceptCall功能移植到舊版上,以實現通話錄音的功能。

舊版linphone被叫通話過程

當有通話呼入時,在界面上滑動接收通話按鈕(accept),即接通電話。按鈕accept的觸摸監聽中主要調用了*answer()*方法。首先實例化一個CallParams對象,設置一些屬性,然後主要調用了LinphoneManager.getInstance().acceptCallWithParams(mCall, params)方法接受通話。

private void answer() {
    if (alreadyAcceptedOrDeniedCall) {
        return;
    }
    alreadyAcceptedOrDeniedCall = true;
    //CallParams是一個包含不同的通話相關參數的接口
    CallParams params = LinphoneManager.getLc().createCallParams(mCall);

    boolean isLowBandwidthConnection = !LinphoneUtils.isHighBandwidthConnection(LinphoneService.instance().getApplicationContext());

    if (params != null) {
        params.enableLowBandwidth(isLowBandwidthConnection);
    } else {
        Log.e("Could not create call params for call");
    }

    if (params == null || !LinphoneManager.getInstance().acceptCallWithParams(mCall, params)) {
        // the above method takes care of Samsung Galaxy S
        Toast.makeText(this, R.string.couldnt_accept_call, Toast.LENGTH_LONG).show();
    } else {
        if (!LinphoneActivity.isInstanciated()) {
            return;
        }
        LinphoneManager.getInstance().routeAudioToReceiver();
        LinphoneActivity.instance().startIncallActivity(mCall);
    }
}

新版linphone被叫通話過程

在accept方法中調用的是LinphoneManager.getCallManager().acceptCall(mCall)方法,在該方法中實例化了CallParams參數,並調用了setRecordFile方法設置錄音文件的路徑。這在老版的linphone中並沒有看到。

public boolean acceptCall(Call call) {
    if (call == null) return false;

    Core core = LinphoneManager.getCore();
    CallParams params = core.createCallParams(call);

    boolean isLowBandwidthConnection =
            !LinphoneUtils.isHighBandwidthConnection(
                    LinphoneContext.instance().getApplicationContext());

    if (params != null) {
        params.enableLowBandwidth(isLowBandwidthConnection);
        params.setRecordFile(
                FileUtils.getCallRecordingFilename(mContext, call.getRemoteAddress()));
    } else {
        Log.e("[Call Manager] Could not create call params for call");
        return false;
    }

    call.acceptWithParams(params);
    return true;
}

在被叫通話時實現錄音

下載舊版的linphone,如release/4.0.1。在Android Studio中打開,等待gradle加載工程。對方有電話呼入時,滑動接受呼叫按鈕後,雙方建立通話連接。我想要在建立通話後即開始錄音,並在點擊通話結束按鈕後停止錄音。錄音文件保存在手機某目錄下,可以直接播放。

開始錄音

首先要找到代碼調用的位置。可以通過佈局文件查找對應的活動(Activity)或者碎片(Fragment),這個佈局文件對應的是活動call/CallIncomingActivity.java.ImageView accept對象通過findViewById(R.id.accept)方法綁定到接受通話按鈕上,通過重寫setOnTouchListener方法處理ImageView上的移動事件,當事件爲**ACTION_MOVE**,並滿足一定條件時,將執行answer()方法。
電話呼入,R.layout.call_incoming

answerRecord()

爲了實現錄音功能,在這裏我用一個answerRecord()方法替代answer()方法。

/**
 * wlf 1127
 */
private void answerRecord() {
    if (alreadyAcceptedOrDeniedCall) {
        return;
    }
    Log.i("wlf","CallIncomingActivity#answerRecord!");
    alreadyAcceptedOrDeniedCall = true;
    if (!CallManager.getInstance().acceptCall(mCall)) {
        // the above method takes care of Samsung Galaxy S
        Toast.makeText(this, R.string.couldnt_accept_call, Toast.LENGTH_LONG).show();
    }
}

代碼很簡單,從新版linphone中扒過來,主要就是調用了CallManager.getInstance().acceptCall(mCall)方法,但是現在CallManager類中並沒有這個方法,需要去實現它。

acceptCall(Call call)

/**
 * wlf 1127
 * @param call
 * @return
 */
public boolean acceptCall(Call call) {
    if (call == null) return false;
    Log.i("wlf","CallManager#acceptCall!");
    CallParams params = LinphoneManager.getLc().createCallParams(call);
    //TODO :params.setMediaEncryption();
    boolean isLowBandwidthConnection =
            !LinphoneUtils.isHighBandwidthConnection(LinphoneService.instance().getApplicationContext());

    if (params != null) {
        params.enableLowBandwidth(isLowBandwidthConnection);
        String filePath = FileUtils.getCallRecordingFilename(LinphoneService.instance().getApplicationContext(), call.getRemoteAddress());
        Log.i("wlf","filePath = " + filePath);
        params.setRecordFile(filePath);
    } else {
        Log.e("[Call Manager] Could not create call params for call");
        return false;
    }
    call.acceptWithParams(params);
    LinphoneManager.getInstance().routeAudioToReceiver();
    LinphoneActivity.instance().startIncallActivity(call);
    call.startRecording();
    Log.i("wlf","after startIncallActivity!");
    return true;
}

首先還是實例化對象CallParams,並設置一些參數。
其中setRecordFile()需要的參數是一個String類型的文件保存路徑,我們通過FileUtils中的getCallRecordingFilename獲取,該工具類只在新版中有,所以直接把整個文件拷貝到org/linphone路徑下。AS會提示一些缺庫錯誤,可以直接把它們註釋掉,保證getCallRecordingFilename方法沒問題就可以。另外,因爲我們只需要錄音,而不需要錄視頻,所以這裏把getCallRecordingFilename方法內的fileName += format.format(new Date()) + ".mkv"改爲fileName += format.format(new Date()) + ".wav"
call對象接受了參數params,就可以啓動通話界面,然後調用call.startRecording()開始錄音了。

結束錄音

通話開始後,將進入活動CallActivity.java
在onClick(View v)中監聽按鈕的點擊事件,結束通話的按鈕是R.id.hang_up。當點擊該按鈕時,將調用hangUp()方法。
通話界面R.id.call

hangUp()

private void hangUp() {
    Core lc = LinphoneManager.getLc();
    Call currentCall = lc.getCurrentCall();
    Call.stopRecording();//wlf 1127
    Log.i("wlf"," CallActivity#hangUp.stopRecording!");
 
    if(crrentCall != null) {
   	 lc.terminateCall(currentCall)
    } else if (lc.isInConference()) {
        lc.terminateConference();
    } else {
        lc.terminateAllCalls();
    }
}

這裏調用LinphoneManager.getLc().getCurrentCall()方法獲取到當前的Call對象,並中斷通話。我們在中斷通話前加入代碼Call.stopRecording()以停止錄音

這樣整個錄音過程就結束了,可以在手機目錄/storage/emulated/0/Linphone/recordings/目錄下找到錄音文件,測試是否錄音成功

TODO

目前只是在被叫通話時測試了錄音方法Call.startRecord()和Call.stopRecord()方法的可用性,方法調用的位置還需要進一步研究。

  1. 增加按鈕,點擊後可開始或者取消錄音
  2. 錄音加密和解密播放。
發佈了30 篇原創文章 · 獲贊 4 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章