首页 » 技术分享 » Linphone探索:7 . 如何进行视频电话

Linphone探索:7 . 如何进行视频电话

 

项目地址:https://git.oschina.net/qin_xiao_yu/Linphone.git


1 . 绪论

  • 本月还有3篇微博需要更新,否则就持之以恒的徽章就没啦,前一段时间都在忙各种事情没有时间更新博客,现在阶段性的不太忙了,补上这月个剩下的3篇,关于Linphone的内容,各位有想了解的,想好题目,在留言你提问,如果我能解答我就出个博客专门说一下,解答不了的也没辙了。

  • 本博客的大部分内容是为了记录项目中的一些和业务不太相关,但是和对应技术比较相关的东西,一方面为了以后能回顾一下,另一方面也能帮其它同学解决点实际问题。

- 整体思路

  • 服务器支持。
  • 终端设置。
  • 拨出视频电话。
  • 接听视频电话
  • 显示双向视频。

- 分步实现

3.1 服务器支持

  • 我使用的服务器是Freeswitch,需要在服务器上设置视频对应的编码方式。
  • 已下配置来自于《FreeSWITCH权威指南》:
    • freeswitch模式支持语音通话,因此默认的通话皆为语音通话,如果需要支持视频通话,只需要在配置文件中增加想关的视频编解码就可以了。
    • freeswitch目前支持的编解码方式有:H261、H263、H264、VP8等。
    • freeswitch只支持视频透传,所以必须电话两侧的终端都为同一种编码方式。
    • 在conf/vars.xml中修改如下
    <X-PRE-PROCESS cmd="set" data="global_codec_prefs=G722,PCMU,PCMA,GSM"/>
    <X-PRE-PROCESS cmd="set" data="outbound_codec_prefs=PCMU,PCMA,GSM"/>
    改为
    <X-PRE-PROCESS cmd="set" data="global_codec_prefs=G722,PCMU,PCMA,GSM,H263,H264,VP8"/>
    <X-PRE-PROCESS cmd="set" data="outbound_codec_prefs=PCMU,PCMA,GSM,H263,H264,VP8"/>
  • 重启服务器,使配置生效

    • 使用sofia status profile internal 查看视频编码方式是否成功配置。

3.2 终端设置

  1. 前提为设备成功启动Linphone service,此时LinphoneCore,LinphoneManager,LinphonePreferences 都已经正常启动。
  2. 使用如下代码配置终端参数
/*获得LinphonePreferences的实体类,这个包含了Linphone的所有参数配置*/
LinphonePreferences mPrefs = LinphonePreferences.instance();
/*设置自动接听视频通话的请求,也就是说只要是视频通话来了,直接就接通,不用按键确定,这是我们的业务流,不用理会*/
mPrefs.setAutomaticallyAcceptVideoRequests(true);
/*设置初始话视频电话,设置了这个你拨号的时候就默认为使用视频发起通话了*/
mPrefs.setInitiateVideoCall(true);
/*这是允许视频通话,这个选了false就彻底不能接听或者拨打视频电话了*/
mPrefs.enableVideo(true);
setCodecMime();

/*
    设置音频的codec,这里我只选择了打开VP8的codec。H264因为硬件平台的原因,无法正常使用decode所以放弃了
*/
private void setCodecMime()
{
    LinphoneCore lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
    for (final PayloadType pt : lc.getVideoCodecs())
    {
        debug.i("setCodecMime = " + pt.getMime());
        if (!pt.getMime().equals("VP8"))
        {
            try
            {
                debug.i("disable codec " + pt.getMime());
                LinphoneManager.getLcIfManagerNotDestroyedOrNull().enablePayloadType(pt, false);
            }
            catch (LinphoneCoreException e)
            {
                Log.e(e);
            }
        }
    }
}

3.3 拨出视频电话

  • 实际上在完成了上述设置后,若对方也打开了视频通话的选项,并且选定了编码方式为VP8后,只要调用拨号的API即可实现拨出视频电话。
  • 使用如下方法即可实现上述功能。
public static void dialNumber(String number)
{
    if (LinphoneManager.getLc().getCallsNb() == 0)
    {
        debug.i("dialNumber(" + number + ");");
        LinphoneManager.getInstance().newOutgoingCall("电话号码", "显示姓名");
    }
}

3.4 接听视频电话

  • 在通话到来时,也就是在callState变成IncomingReceived时,调用answer方法即可实现视频接听。
public static void answer(Context mContext)
{

    if (LinphoneManager.getLcIfManagerNotDestroyedOrNull() != null)
    {
        List<LinphoneCall> calls = LinphoneUtils.getLinphoneCalls(LinphoneManager.getLc());
        for (LinphoneCall call : calls)
        {
            if (State.IncomingReceived == call.getState())
            {
                mCall = call;
                break;
            }
        }
    }

    if (State.IncomingReceived != mCall.getState()) return;

    if (mContext.getPackageManager().checkPermission(Manifest.permission.RECORD_AUDIO, mContext.getPackageName()) == PackageManager.PERMISSION_GRANTED || LinphonePreferences.instance()
            .audioPermAsked())
    {
        // startActivity(new Intent(LinphoneActivity.instance(),
        // CallIncomingActivity.class));
        debug.i("CallIncomingActivity.class");
    }
    else
    {
        debug.i("checkAndRequestAudioPermission(true);");
        checkAndRequestAudioPermission(mContext, true);
    }

    LinphoneCallParams 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;
        // }
        final LinphoneCallParams remoteParams = mCall.getRemoteParams();
        if (remoteParams != null && remoteParams.getVideoEnabled() && LinphonePreferences.instance().shouldAutomaticallyAcceptVideoRequests())
        {
            // LinphoneActivity.instance().startVideoActivity(mCall);
            debug.i("LinphoneActivity.instance().startVideoActivity(mCall)");
            switchVideo(true);
        }
        else
        {
            debug.i("LinphoneActivity.instance().startIncallActivity(mCall);");
            // LinphoneActivity.instance().startIncallActivity(mCall);
            switchVideo(false);
        }
    }
}

private static void switchVideo(final boolean displayVideo)
{
    debug.i("switchVideo");
    final LinphoneCall call = LinphoneManager.getLc().getCurrentCall();
    if (call == null)
    {
        return;
    }

    // Check if the call is not terminated
    if (call.getState() == State.CallEnd || call.getState() == State.CallReleased) return;

    if (!displayVideo)
    {
        // showAudioView();
    }
    else
    {
        if (!call.getRemoteParams().isLowBandwidthEnabled())
        {
            LinphoneManager.getInstance().addVideo();
            debug.i("addVideo");
            // if (videoCallFragment == null ||
            // !videoCallFragment.isVisible())
            // {
            // showVideoView();
            // }
        }
        else
        {
            // displayCustomToast(getString(R.string.error_low_bandwidth),
            // Toast.LENGTH_LONG);
        }
    }
}
  • 上面的代码已经屏蔽了所有的页面跳转的代码。其间比较关键的是在switchVideo方法中的如下部分
if (!call.getRemoteParams().isLowBandwidthEnabled())
{
    LinphoneManager.getInstance().addVideo();
    debug.i("addVideo");
    // if (videoCallFragment == null ||
    // !videoCallFragment.isVisible())
    // {
    // showVideoView();
    // }
}
  • 这里在查看了当前带宽时候能够支持视频通话后,执行LinphoneManager.getInstance().addVideo();添加了视频。

3.4 显示双向视频

  • 在主程序界面放置一个fragment控件。
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@+drawable/background"
    android:gravity="center_horizontal"
    tools:context="com.sxkeda.launcher.MainActivity"
    tools:ignore="MergeRootFrame" >

    <LinearLayout
        android:id="@+id/fragmentContainer2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_alignParentRight="true"
        android:layout_below="@+id/titleBar"
        android:layout_toRightOf="@+id/left_icon_panel"
        android:orientation="vertical" >

    </LinearLayout>
</RelativeLayout>   

这里写图片描述

  • 在电话状态LinphoneCall.State 等于 Connected(接通)和OutgoingProgress(拨出电话)时。在主程序中替换fragment组件。
if (state == LinphoneCall.State.Connected)
{
    LinphoneCall mCall = LinphoneManager.getLc().getCurrentCall();
    /*在接通状态下,必须查看来电的远程参数,确定其带有视频通话的参数才打开视频通话,否则只进行语音通话*/
    final LinphoneCallParams remoteParams = mCall.getRemoteParams();
    debug.i("remoteParams.getVideoEnabled() = "+remoteParams.getVideoEnabled());
    if (remoteParams != null && remoteParams.getVideoEnabled())
    {
        changeFragmentToVideoCall();
    } 
    else
    {
        changeFragmentToAudioCall();
    }
}
if (state == LinphoneCall.State.OutgoingProgress)
{
    debug.i("to CallVideoFragment");
    if (Business.getInstance().isCurrentCallVideo())
    {
        changeFragmentToVideoCall();
    }
    else
    {
        changeFragmentToAudioCall();
    }
}               
private void changeFragmentToVideoCall()
{
    CallVideoFragment videoCallFragment = new CallVideoFragment();
    FragmentTransaction transaction = getFragmentManager().beginTransaction();
    transaction.replace(R.id.fragmentContainer2, videoCallFragment);
    transaction.commit();
}
  • 在切换视频的时候,我们使用了一个fragment为CallVideoFragment,这个组件是Linphone本身就有的组件,我们直接哪来就用了。这个组件自动的将本地摄像头的头像投射到了小窗口,对方视频的图像经过界面投射到了大窗口上。
  • 就是这个文件:

这里写图片描述

4. 谢谢阅读

转载自原文链接, 如需删除请联系管理员。

原文链接:Linphone探索:7 . 如何进行视频电话,转载请注明来源!

1