[转]【linphone】android--ip电话

标签: | 发表时间:2013-10-24 16:22 | 作者:zeo112140
分享到:
出处:http://blog.csdn.net/zeo112140

1. 如何初始化参数?

2.如何打电话?

3.如何接电话?

4.上层和下层如何调用?

5.声音如何接上

==============

android2.3部分的sip实现

ap部分:

1.初始化参数

public void initializeManager() {
        if(manager == null) {
          manager = SipManager.newInstance(this); //初始化call manager.
        }

        initializeLocalProfile(); //初始化profile,也即是参数
    }

…………


    public void initializeLocalProfile() {
        if (manager == null) {
            return;
        }

        if (me != null) {
            closeLocalProfile();
        }

        //得到username,pwd, domain

        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
        String username = prefs.getString("namePref", "");
        String domain = prefs.getString("domainPref", "");
        String password = prefs.getString("passPref", "");

        //check 参数

        if (username.length() == 0 || domain.length() == 0 || password.length() == 0) {
            showDialog(UPDATE_SETTINGS_DIALOG);
            return;
        }

        try {

            //构建profile类
            SipProfile.Builder builder = new SipProfile.Builder(username, domain);
            builder.setPassword(password);
            me = builder.build();

            //构建profile类的包装,并通过manager启动profile的运用

            Intent i = new Intent();
            i.setAction("android.SipDemo.INCOMING_CALL");
            PendingIntent pi = PendingIntent.getBroadcast(this, 0, i, Intent.FILL_IN_DATA);
            manager.open(me, pi, null);

            // This listener must be added AFTER manager.open is called,
            // Otherwise the methods aren't guaranteed to fire.

            //注册回调,监控注册的情况,并在UI上响应。

            manager.setRegistrationListener(me.getUriString(), new SipRegistrationListener() {
                    public void onRegistering(String localProfileUri) {
                        updateStatus("Registering with SIP Server...");
                    }

                    public void onRegistrationDone(String localProfileUri, long expiryTime) {
                        updateStatus("Ready");
                    }

                    public void onRegistrationFailed(String localProfileUri, int errorCode,
                            String errorMessage) {
                        updateStatus("Registration failed.  Please check settings.");
                    }
                });
        } catch (ParseException pe) {
            updateStatus("Connection Error.");
        } catch (SipException se) {
            updateStatus("Connection error.");
        }
    }

2.打电话


    public void initiateCall() {

        //更新UI,显示被叫方的sip地址

        updateStatus(sipAddress);

        try {

            //设置call过程中的回调
            SipAudioCall.Listener listener = new SipAudioCall.Listener() {
                // Much of the client's interaction with the SIP Stack will
                // happen via listeners.  Even making an outgoing call, don't
                // forget to set up a listener to set things up once the call is established.
                @Override
                public void onCallEstablished(SipAudioCall call) {
                    call.startAudio();
                    call.setSpeakerMode(true);
                    call.toggleMute();
                    updateStatus(call);
                }

                @Override
                public void onCallEnded(SipAudioCall call) {
                    updateStatus("Ready.");
                }
            };

            //打出call。

            call = manager.makeAudioCall(me.getUriString(), sipAddress, listener, 30);

        }
        catch (Exception e) {
            Log.i("WalkieTalkieActivity/InitiateCall", "Error when trying to close manager.", e);
            if (me != null) {
                try {
                    manager.close(me.getUriString());
                } catch (Exception ee) {
                    Log.i("WalkieTalkieActivity/InitiateCall",
                            "Error when trying to close manager.", ee);
                    ee.printStackTrace();
                }
            }
            if (call != null) {
                call.close();
            }
        }
    }

3.接电话

构建一个broadcast receiver

public void onReceive(Context context, Intent intent) {
        SipAudioCall incomingCall = null;
        try {

            //call的监听,回调实现

            SipAudioCall.Listener listener = new SipAudioCall.Listener() {
                @Override
                public void onRinging(SipAudioCall call, SipProfile caller) {
                    try {
                        call.answerCall(30);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            };

            WalkieTalkieActivity wtActivity = (WalkieTalkieActivity) context;

            //注册监听

            incomingCall = wtActivity.manager.takeAudioCall(intent, listener);
            incomingCall.answerCall(30);
            incomingCall.startAudio();
            incomingCall.setSpeakerMode(true);
            if(incomingCall.isMuted()) {
                incomingCall.toggleMute();
            }

            wtActivity.call = incomingCall;

            wtActivity.updateStatus(incomingCall);

        } catch (Exception e) {
            if (incomingCall != null) {
                incomingCall.close();
            }
        }
    }

4. 上下层的调用

这个层次的问题在framework中,看framework是如何同协议栈等的交互。

 

linphone的app

1.注册参数

startLibLinphone@linphoneManager.java 启动linphone的lib,同时对proxy进行注册。关键的在于iterate。

 

private synchronized void startLibLinphone(final Context context) {
        try {
            copyAssetsFromPackage(context);

            mLc = LinphoneCoreFactory.instance().createLinphoneCore(
                    this, linphoneConfigFile, linphoneInitialConfigFile, null);

            mLc.enableIpv6(mPref.getBoolean(getString(R.string.pref_ipv6_key), false));

            mLc.setPlaybackGain(3);  
            mLc.setRing(null);

            try {

                //初始化参数从conf中
                initFromConf(context);
            } catch (LinphoneException e) {
                Log.w(TAG, "no config ready yet");
            }
            TimerTask lTask = new TimerTask() {
                @Override
                public void run() {
                    mLc.iterate();
                }
            };

            //

Main loop function. It is crucial that your application call it periodically. #iterate() performs various backgrounds tasks:

  • receiving of SIP messages
  • handles timers and timeout
  • performs registration to proxies

authentication retries The application MUST call this function from periodically, in its main loop.
Be careful that this function must be call from the same thread as other liblinphone methods. In not the case make sure all liblinphone calls are serialized with a mutex.

            //每隔100ms去调用iterate来获得下层的消息。

            mTimer.scheduleAtFixedRate(lTask, 0, 100);

            IntentFilter lFilter = new IntentFilter(Intent.ACTION_SCREEN_ON);
            lFilter.addAction(Intent.ACTION_SCREEN_OFF);
            context.registerReceiver(mKeepAliveReceiver, lFilter);
        }
        catch (Exception e) {
            Log.e(TAG,"Cannot start linphone",e);
        }
    }

 

2 打电话

LinphoneManager.getInstance().newOutgoingCall(mAddress);

……

public void newOutgoingCall(AddressType address) {
        String to = address.getText().toString();

        if (mLc.isIncall()) {
            serviceListener.tryingNewOutgoingCallButAlreadyInCall();
            return;
        }
        LinphoneAddress lAddress;
        try {
            lAddress = mLc.interpretUrl(to);
        } catch (LinphoneCoreException e) {
            serviceListener.tryingNewOutgoingCallButWrongDestinationAddress();
            return;
        }
        lAddress.setDisplayName(address.getDisplayedName());

        try {
            boolean prefVideoEnable = isVideoEnabled();
            boolean prefInitiateWithVideo = mPref.getBoolean(mR.getString(R.string.pref_video_initiate_call_with_video_key), false);
            resetCameraFromPreferences();
            CallManager.getInstance().inviteAddress(lAddress, prefVideoEnable && prefInitiateWithVideo);

        } catch (LinphoneCoreException e) {
            serviceListener.tryingNewOutgoingCallButCannotGetCallParameters();
            return;
        }
    }

……

inviteAddress@CallManager.java

void inviteAddress(LinphoneAddress lAddress, boolean videoEnabled) throws LinphoneCoreException {
    LinphoneCore lc = LinphoneManager.getLc();
    LinphoneCallParams params = lc.createDefaultCallParameters();
    bm().updateWithProfileSettings(lc, params);

    if (videoEnabled && params.getVideoEnabled()) {
        videoManager().setMuted(false);
        params.setVideoEnabled(true);
    } else {
        params.setVideoEnabled(false);
    }

    lc.inviteAddressWithParams(lAddress, params);
}

 

3 接电话

  callState@LinphoneManager.java(由lib回调) --->serviceListener.onCallStateChanged(call, state, message)

onCallStateChanged@LinphoneService.java

public void onCallStateChanged(final LinphoneCall call, final State state, final String message) {
        if (state == LinphoneCall.State.IncomingReceived) {
            //wakeup linphone
            startActivity(new Intent()
                    .setClass(this, LinphoneActivity.class)
                    .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
        } else if (state == LinphoneCall.State.StreamsRunning) {
            if (LinphoneActivity.isInstanciated()
                    && getResources().getBoolean(R.bool.use_video_activity)
                    && call.getCurrentParamsCopy().getVideoEnabled()) {
                LinphoneActivity.instance().startVideoActivity();
            }
        }

        mHandler.post(new Runnable() {
            public void run() {
                if (guiListener() != null)
                    guiListener().onCallStateChanged(call, state, message);           
            }
        });
    }

接受视频部分:整个视频通话界面是一个activity,里面有两个surfaceview,本地的由本地camera capture得来。对方视频由LinphoneCoreImpl.java中

public void setVideoWindow(Object w) {
        if (mVideoWindow!=null)
            mVideoWindow.setListener(null);
        mVideoWindow=new AndroidVideoWindowImpl((SurfaceView) w);
        mVideoWindow.setListener(new AndroidVideoWindowImpl.VideoWindowListener(){
            public void onSurfaceDestroyed(AndroidVideoWindowImpl vw) {
                setVideoWindowId(nativePtr,null);
            }

            public void onSurfaceReady(AndroidVideoWindowImpl vw) {
                setVideoWindowId(nativePtr,vw);
            }
        });
    }

 

 

  • =========

启动linphoneactivity或者启动VideoActivity。同时UI变化

guiListener().onCallStateChanged(call, state, message);

 

===========

     onCallStateChanged@DialActivity.java (UI变化,进入对应的模式)

public void onCallStateChanged(LinphoneCall call, State state, String message) {
    LinphoneCore lc = LinphoneManager.getLc();
    if (state == LinphoneCall.State.OutgoingInit) {
        enterIncallMode(lc);
    } else if (state == LinphoneCall.State.IncomingReceived) {
        callPending(call);
    } else if (state == LinphoneCall.State.Connected) {
        if (call.getDirection() == CallDirection.Incoming) {
            enterIncallMode(lc);
        }
    } else if (state == LinphoneCall.State.Error) {
        if (mWakeLock.isHeld()) mWakeLock.release();
        showToast(R.string.call_error, message);
        exitCallMode();
    } else if (state == LinphoneCall.State.CallEnd) {
        exitCallMode();
    }
}

       

 

4 上下层交互

JNI. && 上层有iterate不断的循环调用下层的方法,从而使上层能获得下层的消息。

 

5 参数变化

a 启动linphone的时候,会初始化一次(从1的分析中可以看出)

b 在preference的onpause中可以看到一次,在preference要退到后台或者要关闭的时候,执行一次参数重新配置。可见并不是点了一项,就应用,而是所有的参数一起应用。

LinphoneManager.getInstance().initFromConf(getApplicationContext());

c firstloginActivity中一次。

 

6 call的状态变化

通过接口interface LinphoneCoreListener来实现,该接口被lib调用

 

7 linphone 中各个文件的作用

org.linphone

AboutActivity.java  关于,不用说

BandwidthManager.java  无继承,设置上行和下行带宽,并且对应设置video的size

BootReceview.java 开启启动接收的recevier,目的是启动linphoneservice

CallManager.java 无继承,Handle call updating, reinvites

 

org.linphone.coreimpl

Hacks.java 工具类,判断手机类型,及摄像头类型

org.linphone.core.video 此包主要处理camera配置,record配置,recordmanager

AndroidcameraConf.java 基类,提供camera conf的基础部件,提供内部类AndroidCameras(camera的最基本实现)

AndroidcameraConf5.java  针对2.3以下的手机,并且对有双摄像头的2.3以下的手机提供支持。

AndroidcameraConf9.java  正对普通的android2.3以上的手机

AndroidCameraRecord.java  抽象类

AndroidCameraRecordImpl.java  实现类,sdk5以上。onPreviewFrame中使用native function

AndroidCameraRecord8.java    继承自AndroidCameraRecordImpl,sdk8以上

AndroidCameraRecord9.java    继承自AndroidCameraRecordImpl,sdk9以上

AndroidCameraRecordManager.java  对外提供接口,对内使用以上类。

org.linphone.core

CallDirection.java 一通电话的方向,incoming后置outgoing

VideoSize.java   控制video的size,给BandwidthManager提供服务

 

 

=============

12.14   IP3088 音频 参数分析

[sound]
playback_dev_id=ANDROID SND: Android Sound card
ringer_dev_id=ANDROID SND: Android Sound card
capture_dev_id=ANDROID SND: Android Sound card
remote_ring=/data/data/org.linphone/files/ringback.wav
local_ring=/data/data/org.linphone/files/oldphone_mono.wav
ec_tail_len=120
ec_framesize=128

el_type=mic
el_thres=0.05
el_force=100000
el_sustain=600
el_transmit_thres=1.7
ng_floorgain=0.01

以上参数来自配置文件,linphonerc, 该文件位于res/raw目录下
  • sound.el_speed  通过配置文件读取,配置文件中没有,取默认值为-1

sound.noisegate  通过配置文件读取,配置文件中没有,取默认值为0

sound.mic_gain   通过配置文件读取,配置文件中没有,取默认值为1

sound.ng_thres   通过配置文件读取,配置文件中没有,取默认值为0.05

sound.el_type    配置文件为mic,通过code判断,其值mic,1

sound.play_gain_level 没有找到

sound.argc       通过配置文件读取,配置文件中没有,取默认值为0

sound.ea         通过配置文件读取,配置文件中没有,取默认值为0

sound.ec         echo cancel 是否打开,由jni向上提供接口

sound.latency    0

 

delay的值通过create thread 计算得知,得知后,会把delay的值写回到config文件中

作者:zeo112140 发表于2013-10-24 16:22:52 原文链接
阅读:8 评论:0 查看评论

相关 [linphone android ip] 推荐:

[转]【linphone】android--ip电话

- - 小鸥的博客
android2.3部分的sip实现.           manager = SipManager.newInstance(this); //初始化call manager.         initializeLocalProfile(); //初始化profile,也即是参数.         //得到username,pwd, domain.

因占用IP地址 普林斯顿大学屏蔽Android设备

- 二宝 - cnBeta.COM
普林斯顿大学无线网络屏蔽了400多台Android设备,原因是Android的DHCP动态主机配置协议存在bug,在DHCP租期过期后仍然会占用着IP地址,干扰网络上其他设备的连接. DHCPv4允许设备自动从网络中租用一个内网IPv4地址,例如租用192.168.1.2六个小时.

tcp/ip调优

- Lucseeker - 在路上
在TCP/IP协议中,TCP协议提供可靠的连接服务,采用三次握手建立一个连接. 第一次握手:建立连接时,客户端发送syn包(syn=x)到服务器,并进入SYN_SEND状态,等待服务器确认;. 第二次握手:服务器收到syn包,必须确认客户的SYN(ack=x+1),同时自己也发送一个SYN包(syn=y),即SYN+ACK包,此时服务器进入SYN_RECV状态;.

获取 WAN IP

- 狗尾草 - LinuxTOY
如果你在 router 或者 firewall 后面,你直接查询 interface ,拿到可能不是 WAN 的 IP. 很久很久以前的一个版本,把它们贴到 .bashrc (Bash 专用) 或者 .profile (非 Bash 专用)里面去. .profile 即可生效,输入 myip 就能拿到 WAN IP.

TCP/IP分享——链路层

- Goingmm - 弯曲评论
在张国荣自尽8周年纪念日,也就是愚人节的前几十分钟,终于把第二章弄完了. 首席似乎不是特别有空,我就斗胆在这里自己发了,从前面2期的反响来看,相当热烈,我也是摆出一副要杀要剐,悉听尊便的架势,这可能是受最近流行霸气外露的影响,批评几句又伤不了皮毛,也影响不了我的工作和正常生活,只要给大家带来快乐,我就很开心,似乎历史上很多想法都是在争吵中诞生的.

一些IP查询网站

- 19GHz - iGFW
一些境内服务器的IP查询网站:. 一些境外服务器的IP查询网站:. https://whoer.net/ (支持https). 各网站查询到的IP归属地可能有差异,以apnic.net为准. 本文原始地址:http://igfw.tk/archives/5611.

IP地址库介绍

- Wolf - 淘宝核心系统团队博客
在CDN系统中,IP地址库在流量调度、网络服务质量监测等方面起到重要的作用,本文主要介绍了IP地址库的功能、内容以及IP地址库的生成和评价方法. 所谓IP地址库,指的是提供了IP地址(段)同地理、ISP、结构等信息映射关系的一组数据. IP地址库应用的场合较为广泛,下面举几个常见的例子:. (1)根据客户的IP地址确定客户来源,为后期的客户行为分析提供基础,例如需要统计学生群体的访问特点,这时我们就是通过分析来自教育网的IP地址的那些请求得到的.

TCP/IP重传超时--RTO

- dennis - 一个故事@MySQL DBA
Shared by 子非鱼 安知余(褚霸). 概述:本文讨论主机在发送一个TCP数据包后,如果迟迟没有收到ACK,主机多久后会重传这个数据包. 主机从发出数据包到第一次TCP重传开始,RFC中这段时间间隔称为retransmission timeout,缩写做RTO. 本文会先看看RFC中如何定义RTO,然后看看Linux中如何实现.

各个IP API接口

- - 谁主沉浮
腾讯的IP地址API接口地址: http://fw.qq.com/ipaddress. 返回的是数据格式为:var IPData = new Array(“114.218.183.139″,””,”江苏省”,”苏州市”);. <script language=”javascript” type=”text/javascript” src=”http://fw.qq.com/ipaddress”></script> <script>document.write(“你的IP是:”+IPData[0]+”,来自:”+IPData[2]);</script>.

IP分片浅析 - Glacier

- - 博客园_首页
在TCP/IP分层中,数据链路层用MTU(Maximum Transmission Unit,最大传输单元)来限制所能传输的数据包大小,MTU是指一次传送的数据最大长度,不包括数据链路层数据帧的帧头,如以太网的MTU为1500字节,实际上数据帧的最大长度为1512字节,其中以太网数据帧的帧头为12字节.