linphone基于sipserver实现音视频对讲

一、技术背景

 

因业务需求需要基于sipserver实现音视频对讲,开源代码linephone即可实现。文末附Demo实现及源码,对讲的实现也基于服务端实现后,提供ip地址才行。

开源库地址:https://github.com/topics/linphone

二、基于安卓客户端实现

添加build.gradle依赖

repositories {
    maven {
        // Replace snapshots by releases for releases !
        url "http://linphone.org/releases/maven_repository/"
    }
}

implementation "org.linphone:linphone-sdk-android:4.3.1"

主要代码示例

package org.linphone.sample;

import android.app.Service;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.widget.Toast;

import androidx.annotation.Nullable;

import org.linphone.core.Call;
import org.linphone.core.CallParams;
import org.linphone.core.Core;
import org.linphone.core.CoreListenerStub;
import org.linphone.core.Factory;
import org.linphone.core.LogCollectionState;
import org.linphone.core.tools.Log;
import org.linphone.mediastream.Version;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Timer;
import java.util.TimerTask;

public class LinphoneService extends Service {
    private static final String START_LINPHONE_LOGS = " ==== Device information dump ====";
    // Keep a static reference to the Service so we can access it from anywhere in the app
    private static LinphoneService sInstance;

    private Handler mHandler;
    private Timer mTimer;

    private Core mCore;
    private CoreListenerStub mCoreListener;

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

    public static LinphoneService getInstance() {
        return sInstance;
    }

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

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

    @Override
    public void onCreate() {
        super.onCreate();

        // The first call to liblinphone SDK MUST BE to a Factory method
        // So let's enable the library debug logs & log collection
        String basePath = getFilesDir().getAbsolutePath();
        Factory.instance().setLogCollectionPath(basePath);
        Factory.instance().enableLogCollection(LogCollectionState.Enabled);
        Factory.instance().setDebugMode(true, getString(R.string.app_name));

        // Dump some useful information about the device we're running on
        Log.i(START_LINPHONE_LOGS);
        dumpDeviceInformation();
        dumpInstalledLinphoneInformation();

        mHandler = new Handler();
        // This will be our main Core listener, it will change activities depending on events
        mCoreListener = new CoreListenerStub() {
            @Override
            public void onCallStateChanged(Core core, Call call, Call.State state, String message) {
                Toast.makeText(LinphoneService.this, message, Toast.LENGTH_SHORT).show();

                if (state == Call.State.IncomingReceived) {
                    Toast.makeText(LinphoneService.this, "Incoming call received, answering it automatically", Toast.LENGTH_LONG).show();
                    // For this sample we will automatically answer incoming calls
                    CallParams params = getCore().createCallParams(call);
                    params.enableVideo(true);
                    call.acceptWithParams(params);
                } else if (state == Call.State.Connected) {
                    // This stats means the call has been established, let's start the call activity
                    Intent intent = new Intent(LinphoneService.this, CallActivity.class);
                    // As it is the Service that is starting the activity, we have to give this flag
                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                    startActivity(intent);
                }
            }
        };

        try {
            // Let's copy some RAW resources to the device
            // The default config file must only be installed once (the first time)
            copyIfNotExist(R.raw.linphonerc_default, basePath + "/.linphonerc");
            // The factory config is used to override any other setting, let's copy it each time
            copyFromPackage(R.raw.linphonerc_factory, "linphonerc");
        } catch (IOException ioe) {
            Log.e(ioe);
        }

        // Create the Core and add our listener
        mCore = Factory.instance()
                .createCore(basePath + "/.linphonerc", basePath + "/linphonerc", this);
        mCore.addListener(mCoreListener);
        // Core is ready to be configured
        configureCore();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        super.onStartCommand(intent, flags, startId);

        // If our Service is already running, no need to continue
        if (sInstance != null) {
            return START_STICKY;
        }

        // Our Service has been started, we can keep our reference on it
        // From now one the Launcher will be able to call onServiceReady()
        sInstance = this;

        // 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);

        return START_STICKY;
    }

    @Override
    public void onDestroy() {
        mCore.removeListener(mCoreListener);
        mTimer.cancel();
        mCore.stop();
        // A stopped Core can be started again
        // To ensure resources are freed, we must ensure it will be garbage collected
        mCore = null;
        // Don't forget to free the singleton as well
        sInstance = null;

        super.onDestroy();
    }

    @Override
    public void onTaskRemoved(Intent rootIntent) {
        // For this sample we will kill the Service at the same time we kill the app
        stopSelf();

        super.onTaskRemoved(rootIntent);
    }

    private void configureCore() {
        // We will create a directory for user signed certificates if needed
        String basePath = getFilesDir().getAbsolutePath();
        String userCerts = basePath + "/user-certs";
        File f = new File(userCerts);
        if (!f.exists()) {
            if (!f.mkdir()) {
                Log.e(userCerts + " can't be created.");
            }
        }
        mCore.setUserCertificatesPath(userCerts);
    }

    private void dumpDeviceInformation() {
        StringBuilder sb = new StringBuilder();
        sb.append("DEVICE=").append(Build.DEVICE).append("\n");
        sb.append("MODEL=").append(Build.MODEL).append("\n");
        sb.append("MANUFACTURER=").append(Build.MANUFACTURER).append("\n");
        sb.append("SDK=").append(Build.VERSION.SDK_INT).append("\n");
        sb.append("Supported ABIs=");
        for (String abi : Version.getCpuAbis()) {
            sb.append(abi).append(", ");
        }
        sb.append("\n");
        Log.i(sb.toString());
    }

    private void dumpInstalledLinphoneInformation() {
        PackageInfo info = null;
        try {
            info = getPackageManager().getPackageInfo(getPackageName(), 0);
        } catch (PackageManager.NameNotFoundException nnfe) {
            Log.e(nnfe);
        }

        if (info != null) {
            Log.i(
                    "[Service] Linphone version is ",
                    info.versionName + " (" + info.versionCode + ")");
        } else {
            Log.i("[Service] Linphone version is unknown");
        }
    }

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

    private void copyFromPackage(int ressourceId, String target) throws IOException {
        FileOutputStream lOutputStream = openFileOutput(target, 0);
        InputStream lInputStream = 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();
    }
}

 CallActivity

package org.linphone.sample;

import android.annotation.TargetApi;
import android.app.Activity;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.view.TextureView;
import android.view.View;
import android.widget.RelativeLayout;

import androidx.annotation.Nullable;

import org.linphone.core.Call;
import org.linphone.core.Core;
import org.linphone.core.CoreListenerStub;
import org.linphone.core.VideoDefinition;
import org.linphone.core.tools.Log;
import org.linphone.mediastream.Version;

public class CallActivity extends Activity {
    // We use 2 TextureView, one for remote video and one for local camera preview
    private TextureView mVideoView;
    private TextureView mCaptureView;

    private CoreListenerStub mCoreListener;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.call);

        mVideoView = findViewById(R.id.videoSurface);
        mCaptureView = findViewById(R.id.videoCaptureSurface);

        Core core = LinphoneService.getCore();
        // We need to tell the core in which to display what
        core.setNativeVideoWindowId(mVideoView);
        core.setNativePreviewWindowId(mCaptureView);

        // Listen for call state changes
        mCoreListener = new CoreListenerStub() {
            @Override
            public void onCallStateChanged(Core core, Call call, Call.State state, String message) {
                if (state == Call.State.End || state == Call.State.Released) {
                    // Once call is finished (end state), terminate the activity
                    // We also check for released state (called a few seconds later) just in case
                    // we missed the first one
                    finish();
                }
            }
        };

        findViewById(R.id.terminate_call).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Core core = LinphoneService.getCore();
                if (core.getCallsNb() > 0) {
                    Call call = core.getCurrentCall();
                    if (call == null) {
                        // Current call can be null if paused for example
                        call = core.getCalls()[0];
                    }
                    call.terminate();
                }
            }
        });
    }

    @Override
    protected void onStart() {
        super.onStart();
    }

    @Override
    protected void onResume() {
        super.onResume();

        LinphoneService.getCore().addListener(mCoreListener);
        resizePreview();
    }

    @Override
    protected void onPause() {
        LinphoneService.getCore().removeListener(mCoreListener);

        super.onPause();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
    }

    @TargetApi(24)
    @Override
    public void onUserLeaveHint() {
        // If the device supports Picture in Picture let's use it
        boolean supportsPip =
                getPackageManager()
                        .hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE);
        Log.i("[Call] Is picture in picture supported: " + supportsPip);
        if (supportsPip && Version.sdkAboveOrEqual(24)) {
            enterPictureInPictureMode();
        }
    }

    @Override
    public void onPictureInPictureModeChanged(
            boolean isInPictureInPictureMode, Configuration newConfig) {
        if (isInPictureInPictureMode) {
            // Currently nothing to do has we only display video
            // But if we had controls or other UI elements we should hide them
        } else {
            // If we did hide something, let's make them visible again
        }
    }

    private void resizePreview() {
        Core core = LinphoneService.getCore();
        if (core.getCallsNb() > 0) {
            Call call = core.getCurrentCall();
            if (call == null) {
                call = core.getCalls()[0];
            }
            if (call == null) return;

            DisplayMetrics metrics = new DisplayMetrics();
            getWindowManager().getDefaultDisplay().getMetrics(metrics);
            int screenHeight = metrics.heightPixels;
            int maxHeight =
                    screenHeight / 4; // Let's take at most 1/4 of the screen for the camera preview

            VideoDefinition videoSize =
                    call.getCurrentParams()
                            .getSentVideoDefinition(); // It already takes care of rotation
            if (videoSize.getWidth() == 0 || videoSize.getHeight() == 0) {
                Log.w(
                        "[Video] Couldn't get sent video definition, using default video definition");
                videoSize = core.getPreferredVideoDefinition();
            }
            int width = videoSize.getWidth();
            int height = videoSize.getHeight();

            Log.d("[Video] Video height is " + height + ", width is " + width);
            width = width * maxHeight / height;
            height = maxHeight;

            if (mCaptureView == null) {
                Log.e("[Video] mCaptureView is null !");
                return;
            }

            RelativeLayout.LayoutParams newLp = new RelativeLayout.LayoutParams(width, height);
            newLp.addRule(
                    RelativeLayout.ALIGN_PARENT_BOTTOM,
                    1); // Clears the rule, as there is no removeRule until API 17.
            newLp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT, 1);
            mCaptureView.setLayoutParams(newLp);
            Log.d("[Video] Video preview size set to " + width + "x" + height);
        }
    }
}

基于TextureView进行视频流渲染

​​​​​​​ 

demo实现及下载 linephone基于sipserver实现安卓音视频对讲功能-Android文档类资源-CSDN下载基于开源库linephone基于sipserver实现安卓音视频对讲功能更多下载资源、学习资料请访问CSDN下载频道.https://download.csdn.net/download/FrancisBingo/68213457

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
用户点击 用户点击 linphone linphone linphone linphone 的图标后就开始了 的图标后就开始了 的图标后就开始了 的图标后就开始了 的图标后就开始了 linphone linphone linphone linphone 软件,这时 软件,这时 软件,这时 软件,这时 软件,这时 linphoneActivity linphoneActivity linphoneActivity linphoneActivity linphoneActivity linphoneActivity linphoneActivity开始运行,它 开始运行,它 开始运行,它 开始运行,它 使 linphoneService linphoneService linphoneService linphoneService linphoneServicelinphoneServicelinphoneService 开始,并做一些 开始,并做一些 开始,并做一些 开始,并做一些 linphone linphone linphone linphone 帐号密码的登录操作同时引导用户进行环境变 帐号密码的登录操作同时引导用户进行环境变 帐号密码的登录操作同时引导用户进行环境变 帐号密码的登录操作同时引导用户进行环境变 帐号密码的登录操作同时引导用户进行环境变 帐号密码的登录操作同时引导用户进行环境变 帐号密码的登录操作同时引导用户进行环境变 帐号密码的登录操作同时引导用户进行环境变 帐号密码的登录操作同时引导用户进行环境变 帐号密码的登录操作同时引导用户进行环境变 量的设置( 量的设置( LinphonePreferencesActivity LinphonePreferencesActivity LinphonePreferencesActivityLinphonePreferencesActivity LinphonePreferencesActivityLinphonePreferencesActivityLinphonePreferencesActivityLinphonePreferencesActivity LinphonePreferencesActivity LinphonePreferencesActivity LinphonePreferencesActivity LinphonePreferencesActivity)。 环境变量都储存在 环境变量都储存在 环境变量都储存在 环境变量都储存在 sharedPreferencessharedPreferences sharedPreferencessharedPreferences sharedPreferencessharedPreferencessharedPreferencessharedPreferences sharedPreferences 中,它是整个工程共享的一变量池。这些环境有 中,它是整个工程共享的一变量池。这些环境有 中,它是整个工程共享的一变量池。这些环境有 中,它是整个工程共享的一变量池。这些环境有 中,它是整个工程共享的一变量池。这些环境有 中,它是整个工程共享的一变量池。这些环境有 中,它是整个工程共享的一变量池。这些环境有 中,它是整个工程共享的一变量池。这些环境有 中,它是整个工程共享的一变量池。这些环境有 中,它是整个工程共享的一变量池。这些环境有 中,它是整个工程共享的一变量池。这些环境有 中,它是整个工程共享的一变量池。这些环境有 中,它是整个工程共享的一变量池。这些环境有 中,它是整个工程共享的一变量池。这些环境有 音频和视编码设置选择,帐号密服务器自动启回校正网络 音频和视编码设置选择,帐号密服务器自动启回校正网络 音频和视编码设置选择,帐号密服务器自动启回校正网络 音频和视编码设置选择,帐号密服务器自动启回校正网络 音频和视编码设置选择,帐号密服务器自动启回校正网络 音频和视编码设置选择,帐号密服务器自动启回校正网络 音频和视编码设置选择,帐号密服务器自动启回校正网络 音频和视编码设置选择,帐号密服务器自动启回校正网络 音频和视编码设置选择,帐号密服务器自动启回校正网络 音频和视编码设置选择,帐号密服务器自动启回校正网络 音频和视编码设置选择,帐号密服务器自动启回校正网络 音频和视编码设置选择,帐号密服务器自动启回校正网络 音频和视编码设置选择,帐号密服务器自动启回校正网络 音频和视编码设置选择,帐号密服务器自动启回校正网络 音频和视编码设置选择,帐号密服务器自动启回校正网络 音频和视编码设置选择,帐号密服务器自动启回校正网络 音频和视编码设置选择,帐号密服务器自动启回校正网络 音频和视编码设置选择,帐号密服务器自
要基于Linphone实现SIP电话通信,可以按照以下步骤: 1. 安装Linphone 在Linux系统中,可以使用包管理器安装Linphone。例如,在Ubuntu中,可以使用以下命令安装: ```shell sudo apt-get install linphone ``` 在Windows系统中,可以从Linphone的官方网站下载安装程序进行安装。 2. 创建SIP账户 要使用Linphone进行SIP电话通信,需要先创建一个SIP账户。可以使用Linphone自带的账户配置向导来创建账户,也可以手动创建。 3. 编写Python脚本 可以使用Python的Linphone模块来控制Linphone进行电话通信。以下是一个简单的Python脚本,可以实现拨打电话和接听电话的功能。 ```python import linphone # 创建Linphone Core对象 lc = linphone.Factory.get().create_core() # 初始化Linphone lc.init(None, None) # 创建SIP账户对象 auth_info = linphone.AuthInfo.new( "sip_username", None, "sip_password", None, None, "sip_domain" ) lc.add_auth_info(auth_info) # 创建SIP代理对象 proxy_cfg = lc.create_proxy_config() proxy_cfg.identity_address = lc.create_address("sip:sip_username@sip_domain") proxy_cfg.server_addr = "sip:sip_domain" lc.add_proxy_config(proxy_cfg) lc.default_proxy_config = proxy_cfg # 监听电话事件 def on_call_state_changed(core, call, state, message): print("Call state:", state.name) lc.callback_call_state_changed = on_call_state_changed # 拨打电话 call_params = lc.create_call_params(None) call = lc.invite_address("sip:someone@sip_domain", call_params) # 接听电话 while True: lc.iterate() ``` 在这个脚本中,首先创建了一个Linphone Core对象,然后初始化Linphone。接着,创建了一个SIP账户对象和一个SIP代理对象,并将它们添加到Linphone中。然后,定义了一个回调函数来处理电话状态的变化,最后使用Linphone拨打电话和接听电话。 4. 运行Python脚本 在终端中运行Python脚本,即可开始使用Linphone进行SIP电话通信。 ```shell python3 sip_phone.py ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FrancisBingo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值