LinphoneCore分析

本文详细解析了LinphoneCore的数据结构,介绍了其内部组件如sal模块、配置信息、媒体流等关键部分。LinphoneCore是负责处理VoIP通话的核心组件。
struct _LinphoneCore
{
    MSList* vtable_refs;
    Sal *sal;
//指针sal 指向sal 数据结构图,通过该指针,就可以找到sal 模块用到的所有数据结构体。

    LinphoneGlobalState state;
    struct _LpConfig *config;
//Config 部分指向linphone 的配置信息,包括网络,sip 协议,rtp 传输,音视频参数以及编解码器信息。

    MSList *default_audio_codecs;
    MSList *default_video_codecs;
    net_config_t net_conf;
    sip_config_t sip_conf;
    rtp_config_t rtp_conf;
    sound_config_t sound_conf;
    video_config_t video_conf;
    codecs_config_t codecs_conf;
    ui_config_t ui_conf;
    autoreplier_config_t autoreplier_conf;
    beauty_config_t beauty_conf;
    LinphoneProxyConfig *default_proxy;
    MSList *friends;
    MSList *auth_info;
    struct _RingStream *ringstream;
    time_t dmfs_playing_start_time;
    LCCallbackObj preview_finished_cb;
    LinphoneCall *current_call;   /* the current call */
    MSList *calls;              /* all the processed calls */

//call 指针挂载了所有的通话,每一路通话都由linphonecall 结构体表示。
//Audiostream 和videostream 保存了媒体信息,a_rtp 和a_rtcp 保存了RTP 相关的信息。

    MSList *queued_calls;   /* used by the autoreplier */
    MSList *call_logs;
    MSList *chatrooms;
    int max_call_logs;
    int missed_calls;
    VideoPreview *previewstream;
    struct _MSEventQueue *msevq;
    LinphoneRtpTransportFactories *rtptf;
    MSList *bl_reqs;
    MSList *subscribers;    /* unknown subscribers */
    int minutes_away;
    LinphonePresenceModel *presence_model;
    void *data;
    char *play_file;
    char *rec_file;
    time_t prevtime;
    int audio_bw; /*IP bw consumed by audio codec, set as soon as used codec is known, its purpose is to know the remaining bw for video*/
    LinphoneCoreWaitingCallback wait_cb;
    void *wait_ctx;
    void *video_window_id;
    void *preview_window_id;
    time_t netup_time; /*time when network went reachable */
    struct _EcCalibrator *ecc;
    MSList *hooks;
    LinphoneConference conf_ctx;
    char* zrtp_secrets_cache;
    char* user_certificates_path;
    LinphoneVideoPolicy video_policy;
    time_t network_last_check;
    time_t register_last_check;

    bool_t use_files;
    bool_t apply_nat_settings;
    bool_t initial_subscribes_sent;
    bool_t bl_refresh;

    bool_t preview_finished;
    bool_t auto_net_state_mon;
    bool_t network_reachable;
    bool_t network_reachable_to_be_notified; /*set to true when state must be notified in next iterate*/

    bool_t use_preview_window;
    bool_t network_last_status;
    bool_t ringstream_autorelease;
    bool_t vtables_running;
    char localip[LINPHONE_IPADDR_SIZE];
    int device_rotation;
    int max_calls;
    LinphoneTunnel *tunnel;
    char* device_id;
    MSList *last_recv_msg_ids;
    char *chat_db_file;
    void * beauty_window_id;
#ifdef MSG_STORAGE_ENABLED
    sqlite3 *db;
    bool_t debug_storage;
#endif
#ifdef BUILD_UPNP
    UpnpContext *upnp;
#endif //BUILD_UPNP
    belle_http_provider_t *http_provider;
    belle_tls_verify_policy_t *http_verify_policy;
    MSList *tones;
    LinphoneReason chat_deny_code;
    char *file_transfer_server;
    const char **supported_formats;
    LinphoneContent *log_collection_upload_information;
    LinphoneCoreVTable *current_vtable; // the latest vtable to call a callback, see linphone_core_get_current_vtable
#ifdef ANDROID
    jobject wifi_lock;
    jclass wifi_lock_class;
    jmethodID wifi_lock_acquire_id;
    jmethodID wifi_lock_release_id;
    jobject multicast_lock;
    jclass multicast_lock_class;
    jmethodID multicast_lock_acquire_id;
    jmethodID multicast_lock_release_id;
#endif
};
分析下列Android中实现p2p模式局域网通话代码,为何总是拨号失败,目标sip地址输入是正确的: package com.example.android_demo.linphone; import android.content.Intent; import android.os.Bundle; import android.util.Log; import android.widget.Button; import android.widget.EditText; import android.widget.Toast; import androidx.appcompat.app.AppCompatActivity; import com.example.android_demo.R; import org.linphone.core.Call; import org.linphone.core.Core; import org.linphone.core.CoreListenerStub; import org.linphone.core.Factory; import org.linphone.core.ProxyConfig; import org.linphone.core.RegistrationState; import java.io.IOException; import java.net.InetSocketAddress; import java.net.Socket; public class LinphoneActivity extends AppCompatActivity { private static final String TAG = "LinphoneP2P"; private Core linphoneCore; // Linphone Core 对象,用于管理 SIP 功能 private CoreListenerStub coreListener; // 监听器,用于处理状态回调 private EditText sipAddressInput; // 输入对方 SIP 地址的文本框 private Button callButton; // 呼叫按钮 private Button hangupButton; // 挂断按钮 private String ip; private String username; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_linphone); Intent intent = getIntent(); ip = getIntent().getStringExtra("ip"); username = getIntent().getStringExtra("username"); // 初始化 UI 元素 sipAddressInput = findViewById(R.id.sip_address_input); callButton = findViewById(R.id.call_button); hangupButton = findViewById(R.id.hangup_button); String idAddress = "sip:phone@192.168.0.25"; if(ip.equals("192.168.0.25")){ idAddress = "sip:device@192.168.0.79"; } sipAddressInput.setText(idAddress); // 初始化 Linphone Core setupLinphoneCore(); // 设置按钮点击事件 callButton.setOnClickListener(v -> makeCall()); hangupButton.setOnClickListener(v -> hangupCall()); } /** * 初始化 Linphone Core 和监听器 */ private void setupLinphoneCore() { Factory factory = Factory.instance(); // factory.setLogCollectionEnabled(true); // 启用日志记录 factory.enableLogcatLogs(true); linphoneCore = factory.createCore(null, null, this); // 创建 Core 对象 // 设置监听器以处理回调 coreListener = new CoreListenerStub() { @Override public void onRegistrationStateChanged(Core core, ProxyConfig cfg, RegistrationState state, String message) { Log.d(TAG, "Registration state changed: " + state); if (state == RegistrationState.Ok) { Toast.makeText(LinphoneActivity.this, "注册成功!", Toast.LENGTH_SHORT).show(); } else if (state == RegistrationState.Failed) { Toast.makeText(LinphoneActivity.this, "注册失败:" + message, Toast.LENGTH_SHORT).show(); } } @Override public void onCallStateChanged(Core core, Call call, Call.State state, String message) { Log.d(TAG, "Call state changed: " + state); switch (state) { case IncomingReceived: // 收到来电,接听 try { call.accept(); // 使用 Call 对象的 accept() 方法接听 Toast.makeText(LinphoneActivity.this, "来电接听中...", Toast.LENGTH_SHORT).show(); } catch (Exception e) { Log.e(TAG, "接听电话失败: " + e.getMessage()); } break; case Connected: // 通话已连接 Toast.makeText(LinphoneActivity.this, "通话已连接", Toast.LENGTH_SHORT).show(); break; case End: case Error: // 通话结束或出错 Toast.makeText(LinphoneActivity.this, "通话结束", Toast.LENGTH_SHORT).show(); break; } } }; linphoneCore.addListener(coreListener); // 配置 SIP 信息 setupSipAccount(); } /** * 设置 SIP 账号(P2P 模式无需注册 SIP 服务器) */ private void setupSipAccount() { /* String deviceIp="192.168.0.79"; String phoneIp="192.168.0.25"; String ip = phoneIp; String username = ip.equals(deviceIp)?"device":"phone";*/ // 禁用代理配置(P2P 模式) ProxyConfig proxyConfig = linphoneCore.createProxyConfig(); proxyConfig.setIdentityAddress(linphoneCore.createAddress("sip:"+username+"@"+ip)); proxyConfig.setServerAddr(""); // 不使用任何 SIP 服务器 proxyConfig.setRoute(""); // 不设置路由 proxyConfig.enableRegister(false); // 设置为不注册 // proxyConfig.setRegisterEnabled(false); linphoneCore.setDefaultProxyConfig(proxyConfig); // 设置默认配置 } /** * 发起呼叫 */ private void makeCall() { String sipAddress = sipAddressInput.getText().toString().trim(); if (!isNetworkReachable()) { Toast.makeText(this, "网络不可达,请检查网络连接", Toast.LENGTH_SHORT).show(); return; } if (!isSipServiceAvailable(sipAddress)) { Toast.makeText(this, "目标设备的 SIP 服务不可用", Toast.LENGTH_SHORT).show(); return; } String deviceIp="192.168.0.79"; String phoneIp="192.168.0.25"; String tip = ip.equals(deviceIp)?phoneIp:deviceIp; if (!isPortReachable(tip, 5060)) { Toast.makeText(this, "目标设备的端口不可达", Toast.LENGTH_SHORT).show(); return; } try { Call call = linphoneCore.invite(sipAddress); // 发起呼叫 if (call == null) { Toast.makeText(this, "呼叫失败", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(this, "正在呼叫 " + sipAddress, Toast.LENGTH_SHORT).show(); } } catch (Exception e) { Log.e(TAG, "呼叫失败: " + e.getMessage()); Toast.makeText(this, "呼叫失败:" + e.getMessage(), Toast.LENGTH_SHORT).show(); } } /** * 挂断呼叫 */ private void hangupCall() { Call call = linphoneCore.getCurrentCall(); if (call != null) { linphoneCore.terminateAllCalls(); // linphoneCore.terminateCall(call); // 挂断当前通话 Toast.makeText(this, "通话已挂断", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(this, "当前没有进行中的通话", Toast.LENGTH_SHORT).show(); } } @Override protected void onResume() { super.onResume(); linphoneCore.start(); // 启动 Core } @Override protected void onPause() { linphoneCore.stop(); // 暂停 Core super.onPause(); } @Override protected void onDestroy() { linphoneCore.removeListener(coreListener); linphoneCore.stop(); // 停止 Core linphoneCore = null; super.onDestroy(); } private boolean isNetworkReachable() { return linphoneCore.isNetworkReachable(); } private boolean isSipServiceAvailable(String sipAddress) { try { Call call = linphoneCore.invite(sipAddress); if (call == null) { Log.e(TAG, "呼叫返回 null,目标设备不可用"); return false; } // 立即终止呼叫以避免实际通话 linphoneCore.terminateAllCalls(); return true; // 如果没有异常抛出,目标设备可用 } catch (Exception e) { Log.e(TAG, "目标设备不可用: " + e.getMessage()); return false; } } private boolean isPortReachable(String ip, int port) { try (Socket socket = new Socket()) { socket.connect(new InetSocketAddress(ip, port), 2000); return true; // 端口可达 } catch (IOException e) { Log.e(TAG, "端口不可达: " + e.getMessage()); e.printStackTrace(); } catch (Exception e){ Log.e(TAG, "e端口不可达: " + e.getMessage()); e.printStackTrace(); } return false; } }
05-17
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值