【Android網(wǎng)絡(luò)通話】關(guān)于語音通話LinPhone筆記_android(一)

Linphone是一款開源基于SIP協(xié)議的語音視頻電話軟件,可移植到移動端Android、IOS、WindowsPhone8,桌面系統(tǒng)包括GNU/Linux、Windows、Mac,以及Web瀏覽器。

文檔直連

1、linehone官網(wǎng) : http://www.linphone.org/technical-corner/liblinphone
2、官網(wǎng)-android文檔: https://wiki.linphone.org/xwiki/wiki/public/view/Lib/Getting%20started/Android/
3、官網(wǎng)-android案例: https://github.com/BelledonneCommunications/linphone-android
4、可以直接從maven庫下載android的aar文件
https://linphone.org/releases/maven_repository/org/linphone/linphone-sdk-android/
5、Android基于網(wǎng)絡(luò)的VoIP電話的實現(xiàn)linphone https://blog.csdn.net/weixin_39947864/article/details/81910485
6、Linphone探索:1 . Linphone官方源碼探究 https://blog.csdn.net/u012812482/article/details/51491226

以下內(nèi)容分為兩個部分,第一部分運(yùn)行官方demo,簡單了解linphone的語音通話功能;第二部分就是在自己的項目中集成linphone庫,并自定義linphone的工具類庫便于直接使用。【注意點(diǎn):Android6.0之后的項目要動態(tài)授權(quán)--語音權(quán)限】

第一部分:運(yùn)行官方簡單的demo

首先下載liphone-android(下載)編譯項目,simple文件就是linphone語音通話最簡單的演示,界面如下:

image.png

image.png

官方的項目中還包含詳細(xì)的工具庫,可參考。

Linphone官方源碼探究

以下內(nèi)容來源:Linphone探索 https://blog.csdn.net/u012812482/article/details/51491226

BluetoothManager:藍(lán)牙管理器。
BootReceiver:繼承自BroadcastReceiver的類,用于在設(shè)備啟動時自動啟動LinphoneService。
CallActivity:通話界面。
CallAudioFragment:通話音頻界面。
CallIncomingActivity:來電界面。電話的接聽,掛斷。 當(dāng)前沒有活動電話的情況下:可以通過按鍵掛斷和接聽來電。
通過LinphoneCoreListenerBase類,復(fù)寫callState(電話方法)監(jiān)聽liphone內(nèi)核電話狀態(tài),如果電話已經(jīng)被掛斷(分兩種情況,對方掛斷,本方掛斷)則掛斷電話。如果linphone內(nèi)核已經(jīng)檢測到音頻流(這里是鈴聲的流)則使linphone內(nèi)核打開手機(jī)的喇叭,此時來電鈴聲就會播放。
CallManager:通話管理。
inviteAddress:向某個地址發(fā)起invite 請求
reinviteWithVideo:向當(dāng)前的音頻通路發(fā)起視頻的invite請求,若當(dāng)前帶寬太窄或當(dāng)前通路已經(jīng)有視頻流,這不發(fā)起invite請求。
reinvite:根據(jù)當(dāng)前的profile向當(dāng)前通路發(fā)起invite請求。
updateCall:改變當(dāng)前視頻通話的視頻尺寸,調(diào)用該方法將會在重新建立流媒體通道和重新設(shè)置電話參數(shù)時重新打開攝像頭。
CallOutgoingActivity:撥出電話界面。
CallVideoFragment:視頻通話界面。 在當(dāng)前的Activity中放置了一個繼承自viewsurface的控件,用來繪制視頻界面。
LinphoneManager:Linphone管理器。
啟動管理:linphone內(nèi)核(core),
文件管理:來電鈴聲、信息鈴聲、暫停鈴聲、配置文件
在線狀態(tài):在線,離線。
網(wǎng)絡(luò)狀態(tài):網(wǎng)絡(luò)狀態(tài)發(fā)生變化。
通話管理:撥出,掛斷,DTMF,接聽。
短消息:狀態(tài)
設(shè)備管理:攝像頭,
編碼器管理:音、視頻編碼器的檢測
通道管理(Tunnel):
音量管理
音頻通道:喇叭或者藍(lán)牙
鈴聲管理:啟動或者停止

第二部分,在自己項目中集成linphone庫,語音通話界面如下:

image.png

image.png

自己項目具體實現(xiàn)

(1)添加Linphone的依賴庫

如何在自己的項目中,引入linphone的庫,具體步驟如下:
1、在項目的build.gradle文件中,添加

buildscript {
    repositories {
        jcenter()
        google()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.0.1'
    }
}

allprojects {
    repositories {
        jcenter()
        google()
        //linphone
        maven {
            url "https://linphone.org/releases/maven_repository/"
        }
    }
}

2、在model項目中的build.gradle文件,添加:

dependencies {
    //linphone
    debugImplementation "org.linphone:linphone-sdk-android-debug:4.2+"
    releaseImplementation "org.linphone:linphone-sdk-android:4.2+"

    //權(quán)限管理
    implementation 'com.yanzhenjie:permission:2.0.3'
}
(2)代碼實現(xiàn)
LinphoneService
public class LinphoneService extends Service {
    private static LinphoneService sInstance;
    private static PhoneServiceCallback sPhoneServiceCallback;

    private Core mCore;

    public static void addCallback(PhoneServiceCallback phoneServiceCallback) {
        sPhoneServiceCallback = phoneServiceCallback;
    }

    public static boolean isReady() {
        return sInstance != null;
    }

    public static Core getCore() {
        return sInstance.mCore;
    }

    //監(jiān)聽
    private CoreListenerStub mCoreListnerStub = new CoreListenerStub() {
        /**
         *  通話狀態(tài)
         * @param lc
         * @param call
         * @param cstate
         * @param message
         */
        @Override
        public void onCallStateChanged(Core lc, Call call, Call.State cstate, String message) {
            Log.i("zss", "---- 通話狀態(tài)  [ 狀態(tài):" + cstate + "  ;消息:  " + message + " ]");
            if (cstate == Call.State.IncomingReceived) { //來電
             //   Log.i("zss","----- getRemoteAddress().getUsername: " + call.getRemoteAddress().getUsername() + "  getRemoteAddress().getDomain: " +  call.getRemoteAddress().getDomain() + "  getRemoteAddress().getDisplayName:" + call.getRemoteAddress().getDisplayName() + "  getRemoteAddress().getPort:" + call.getRemoteAddress().getPort() + "  getUsername: " + call.getRemoteAddress().getPassword() );
            //    Log.i("zss", "----getTlsCert: " + authInfo[i].getTlsCert() + "   getTlsCertPath:" + authInfo[i].getTlsCertPath());

                Intent intent = new Intent(LinphoneService.this, CustomReceiveActivity.class);
                CustomReceiveActivity.getReceivedCallFromService(call);
                ReceiveDataModel receiveDataModel = new ReceiveDataModel();
                receiveDataModel.setActiveCall(false);
                receiveDataModel.setNum(call.getRemoteAddress().getUsername());
                intent.putExtra("ReceiveDataModel", receiveDataModel);

                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                startActivity(intent);

//                if (null != sPhoneServiceCallback) {
//                    Log.i("zss", "---- sPhoneServiceCallback ");
//                    sPhoneServiceCallback.incomingCall(call);
//                }
            } else if (cstate == Call.State.OutgoingProgress) { //正在呼叫

            } else if (cstate == Call.State.Connected) { //接通或者拒絕
                if (null != sPhoneServiceCallback) {
                    sPhoneServiceCallback.callConnected();
                }
            } else if (cstate == Call.State.End || (cstate == Call.State.Released)) { //掛斷,未接
                if (null != sPhoneServiceCallback) {
                    sPhoneServiceCallback.callReleased();
                }
            }
        }

        /**
         * 注冊狀態(tài)
         * @param lc
         * @param cfg
         * @param cstate
         * @param message
         */
        @Override
        public void onRegistrationStateChanged(Core lc, ProxyConfig cfg, RegistrationState cstate, String message) {
            if (null != sPhoneServiceCallback) {
                sPhoneServiceCallback.onRegistrationStateChanged(lc, cfg, cstate, message);
            }
        }


    };

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.i("zss", "---- Service_onCreate ");
        LinphoneManager.createAndStart(LinphoneService.this, mCoreListnerStub);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        super.onStartCommand(intent, flags, startId);
        Log.i("zss", "---- Service_onStartCommand ");
        // If our Service is already running, no need to continue
        if (sInstance != null) {
            return START_STICKY;
        }
        sInstance = this;


        return START_STICKY;
    }

    @Override
    public void onDestroy() {
        Log.i("zss", "---- Service_onDestroy ");
        sInstance = null;
        LinphoneManager.destroy();
        super.onDestroy();
    }

    @Override
    public void onTaskRemoved(Intent rootIntent) {
        Log.i("zss", "---- Service_onTaskRemoved ");
        sInstance = null;
        LinphoneManager.destroy();
        // For this sample we will kill the Service at the same time we kill the app
        stopSelf();
        super.onTaskRemoved(rootIntent);
    }
}
LinphoneManager
/**
 * 初始化 linphone
 */
public class LinphoneManager {
    private Context mServiceContext;
    private static LinphoneManager instance;
    private static boolean sExited;

    private String mLinphoneFactoryConfigFile = null;
    public String mLinphoneConfigFile = null;

    private String mLPConfigXsd = null;
    private String mLinphoneRootCaFile = null;
    private String mRingSoundFile = null;
    private String mRingBackSoundFile = null;
    private String mPauseSoundFile = null;
    private String mChatDatabaseFile = null;
    private String mUserCerts = null;

    private Resources mResources;


    private Core mCore;
    private CoreListener mCoreListener;

    private Timer mTimer;
    private Handler mHandler;

    public LinphoneManager(Context serviceContext) {
        mServiceContext = serviceContext;
        sExited = false;

        String basePath = mServiceContext.getFilesDir().getAbsolutePath();
        mLPConfigXsd = basePath + "/lpconfig.xsd";
        mLinphoneFactoryConfigFile = basePath + "/linphonerc";
        mLinphoneConfigFile = basePath + "/.linphonerc";

        mLinphoneRootCaFile = basePath + "/rootca.pem";
        mRingSoundFile = basePath + "/dont_wait_too_long.mkv"; //dont_wait_too_long.mkv   oldphone_mono.wav
        mRingBackSoundFile = basePath + "/ringback.wav";
        mPauseSoundFile = basePath + "/toy_mono.wav";
        mChatDatabaseFile = basePath + "/linphone-history.db";
        mUserCerts = basePath + "/user-certs";

//      mErrorToneFile = basePath + "/error.wav";

        mResources = serviceContext.getResources();

        Factory.instance().setLogCollectionPath(basePath);
        Factory.instance().enableLogCollection(LogCollectionState.Enabled); //日志開關(guān)
        Factory.instance().setDebugMode(true, "Linphone");

        mHandler = new Handler();

    }

    public synchronized static final LinphoneManager createAndStart(Context context, CoreListener coreListener) {
        if (instance != null) {
            throw new RuntimeException("Linphone Manager is already initialized");
        }

        instance = new LinphoneManager(context);
        instance.startLibLinphone(context, coreListener);
        return instance;
    }

    private synchronized void startLibLinphone(Context context, CoreListener coreListener) {
        try {
            mCoreListener = coreListener;
            copyAssetsFromPackage();

            // Create the Core and add our listener
            mCore = Factory.instance().createCore(mLinphoneConfigFile, mLinphoneFactoryConfigFile, context);
            mCore.addListener(coreListener);

            initLibLinphone();

            // Core must be started after being created and configured
            mCore.start();
            // We also MUST call the iterate() method of the Core on a regular basis
            TimerTask lTask = new TimerTask() {
                @Override
                public void run() {
                    mHandler.post(new Runnable() {
                        @Override
                        public void run() {
                            if (mCore != null) {
                                mCore.iterate();
                            }
                        }
                    });
                }
            };
            mTimer = new Timer("Linphone scheduler");
            mTimer.schedule(lTask, 0, 20);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void copyAssetsFromPackage() throws IOException {
      //   LinphoneUtils.copyIfNotExist(mServiceContext, R.raw.dont_wait_too_long, mRingSoundFile);
        //   LinphoneUtils.copyIfNotExist(mServiceContext, R.raw.ringback, mRingBackSoundFile);
        //  LinphoneUtils.copyIfNotExist(mServiceContext, R.raw.toy_mono, mPauseSoundFile);
        LinphoneUtils.copyIfNotExist(mServiceContext, R.raw.linphonerc_default, mLinphoneConfigFile);
        LinphoneUtils.copyIfNotExist(mServiceContext, R.raw.linphonerc_factory, new File(mLinphoneFactoryConfigFile).getName());
        //   LinphoneUtils.copyIfNotExist(mServiceContext, R.raw.lpconfig, mLPConfigXsd);
        //   LinphoneUtils.copyIfNotExist(mServiceContext, R.raw.rootca, mLinphoneRootCaFile);
    }

    private void initLibLinphone() {
        File f = new File(mUserCerts);
        if (!f.exists()) {
            if (!f.mkdir()) {
                Log.e(mUserCerts + " can't be created.");
            }
        }
        mCore.setUserCertificatesPath(mUserCerts);

    }


    public static synchronized Core getCoreIfManagerNotDestroyOrNull() {
        if (sExited || instance == null) {
            Log.e("Trying to get linphone core while LinphoneManager already destroyed or not created");
            return null;
        }
        return getCore();
    }

    public static synchronized final Core getCore() {
        return getInstance().mCore;
    }


    public static final boolean isInstanceiated() {
        return instance != null;
    }

    public static synchronized final LinphoneManager getInstance() {
        if (instance != null) {
            return instance;
        }
        if (sExited) {
            throw new RuntimeException("Linphone Manager was already destroyed. " + "Better use getLcIfManagerNotDestroyed and check returned value");
        }
        throw new RuntimeException("Linphone Manager should be created before accessed");
    }

    public static synchronized void destroy() {
        if (instance == null) {
            return;
        }
        sExited = true;
        instance.doDestroy();
    }

    private void doDestroy() {
        try {
            mCore.removeListener(mCoreListener);
            mTimer.cancel();
            mCore.stop();
        } catch (RuntimeException e) {
            e.printStackTrace();
        } finally {
            mCore = null;
            instance = null;
        }
    }

}
PhoneServiceCallback
public abstract class PhoneServiceCallback {
    /**
     * 注冊狀態(tài)
     */
    public void onRegistrationStateChanged(Core lc, ProxyConfig cfg, RegistrationState cstate, String message) {
    }


    /**
     * 來電狀態(tài)
     *
     * @param linphoneCall
     */
    public void incomingCall(Call linphoneCall) {
    }

    /**
     * 電話接通
     */
    public void callConnected() {
    }

    /**
     * 電話被掛斷
     */
    public void callReleased() {
    }

    /**
     * 正在呼叫
     */
    public void OutgoingProgress(){
    }

}
LinphoneUtils
public class LinphoneUtils {


    public static void copyIfNotExist(Context context, int ressourceId, String target) throws IOException {
        File lFileToCopy = new File(target);
        if (!lFileToCopy.exists()) {
            copyFromPackage(context, ressourceId, lFileToCopy.getName());
        }
    }

    public static void copyFromPackage(Context context, int ressourceId, String target) throws IOException {
        FileOutputStream lOutputStream = context.openFileOutput(target, 0);
        InputStream lInputStream = context.getResources().openRawResource(ressourceId);
        int readByte;
        byte[] buff = new byte[8048];
        while ((readByte = lInputStream.read(buff)) != -1) {
            lOutputStream.write(buff, 0, readByte);
        }
        lOutputStream.flush();
        lOutputStream.close();
        lInputStream.close();
    }
}
PhoneVoiceUtils
public class PhoneVoiceUtils {

    private static volatile PhoneVoiceUtils sPhoneVoiceUtils;
    private Core mLinphoneCore = null;

    public static PhoneVoiceUtils getInstance() {
        if (sPhoneVoiceUtils == null) {
            synchronized (PhoneVoiceUtils.class) {
                if (sPhoneVoiceUtils == null) {
                    sPhoneVoiceUtils = new PhoneVoiceUtils();
                }
            }
        }
        return sPhoneVoiceUtils;
    }

    private PhoneVoiceUtils() {
        mLinphoneCore = LinphoneManager.getCore();
//        mLinphoneCore.enableEchoCancellation(true);
//        mLinphoneCore.enableEchoLimiter(true);
    }

    /**
     * 注冊到服務(wù)器
     *
     * @param name     賬號名
     * @param password 密碼
     * @param host     IP地址:端口號
     */
    public void registerUserAuth(String name, String password, String host) {
        registerUserAuth(name, password, host, TransportType.Udp);
    }

    /**
     * 注冊到服務(wù)器
     *
     * @param name     賬號名
     * @param password 密碼
     * @param host     IP地址:端口號
     * @param type     TransportType.Udp TransportType.Tcp TransportType.Tls
     */
    public void registerUserAuth(String name, String password, String host, TransportType type) {
        //    String identify = "sip:" + name + "@" + host;
        AccountCreator mAccountCreator = mLinphoneCore.createAccountCreator(null);

        mAccountCreator.setUsername(name);
        mAccountCreator.setDomain(host);
        mAccountCreator.setPassword(password);
        mAccountCreator.setTransport(type);

        ProxyConfig cfg = mAccountCreator.createProxyConfig();
        // Make sure the newly created one is the default
        mLinphoneCore.setDefaultProxyConfig(cfg);
    }

    //取消注冊
    public void unRegisterUserAuth() {
        mLinphoneCore.clearAllAuthInfo();

    }

    /**
     * 是否已經(jīng)注冊了
     *
     * @return
     */
    public boolean isRegistered() {
        AuthInfo[] authInfos = mLinphoneCore.getAuthInfoList();
        if (authInfos.length > 0) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 撥打電話
     *
     * @param phone 手機(jī)號
     * @return
     */
    public Call startSingleCallingTo(String phone) {
        Call call = null;
        try {
            Address addressToCall = mLinphoneCore.interpretUrl(phone);

            CallParams params = mLinphoneCore.createCallParams(null);

            params.enableVideo(false); //不可視頻

            if (addressToCall != null) {
                call = mLinphoneCore.inviteAddressWithParams(addressToCall, params);
            }
        } catch (Exception e) {
            e.printStackTrace();

        }
        return call;
    }

    /**
     * 掛斷電話
     */
    public void hangUp() {
        if (mLinphoneCore == null) {
            mLinphoneCore = LinphoneManager.getCore();
        }

        Call currentCall = mLinphoneCore.getCurrentCall();
        if (currentCall != null) {
            mLinphoneCore.terminateCall(currentCall);
        } else if (mLinphoneCore.isInConference()) {
            mLinphoneCore.terminateConference();
        } else {
            mLinphoneCore.terminateAllCalls();
        }

    }

    /**
     * 是否靜音
     *
     * @param isMicMuted
     */
    public void toggleMicro(boolean isMicMuted) {
        if (mLinphoneCore == null) {
            mLinphoneCore = LinphoneManager.getCore();
        }
        mLinphoneCore.enableMic(isMicMuted);
    }

    /**
     * 接聽來電
     *
     * @param
     */
    public void receiveCall(Call call) {
        if (mLinphoneCore == null) {
            mLinphoneCore = LinphoneManager.getCore();
        }
        CallParams params = mLinphoneCore.createCallParams(call);
        params.enableVideo(false);
        if (null != call) {
            call.acceptWithParams(params);
        }
    }
    
}

(項目代碼鏈接)[https://download.csdn.net/download/Android_points/11983375]

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容