Android无需访问互联网的VOIP应用程序

11

我需要开发两个安卓设备之间的VOIP应用程序。
据我所知,有一个SIP协议可用于此目的,但它需要在SIP服务器上注册并访问互联网进行SIP信令。
是否有方法可以在安卓设备上创建没有互联网访问权限的VOIP应用程序?


你的意思是像对讲机一样吗?使用WiFi和蓝牙可以进行点对点通信,这是否能够满足您的需求? - MikeIsrael
实际上我需要一对多的对讲机,带有wifi功能。这是一种没有互联网访问的会议形式。 - Costa Mirkin
SIP不需要互联网访问,也不需要除用户代理之外的任何东西。(这些东西是“有用”的,但不是“必需的”。) - Frank Shearar
@FrankShearar,您能描述一下如何实现吗?请看我的问题:https://dev59.com/BbTps4cB2Jgan1znVpGw - user2808671
现在怎么样,@CostaMirkin? - gumuruh
5个回答

7
当然可以!为什么需要互联网呢?只要您们都连接到同一个网络就可以了!下面是一款可用的应用程序的Java和XML代码。
启动时,它将为您提供自己的本地端口,例如“52022”...每次随机生成,无法更改。然后,我们输入另一部手机的IP地址和他们随机生成的端口号,然后点击连接。他们也这样做,恭喜你们已经连接成功了!这个测试应用程序显然需要您彼此靠近交换端口号,但在我的正式应用程序中,我很容易在连接之前请求每个端口号。希望这可以帮助到您!
public class MainActivity extends Activity {

AudioGroup m_AudioGroup;
AudioStream m_AudioStream;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
      StrictMode.setThreadPolicy(policy);
      try {   
          AudioManager audio =  (AudioManager) getSystemService(Context.AUDIO_SERVICE); 
          audio.setMode(AudioManager.MODE_IN_COMMUNICATION);
          m_AudioGroup = new AudioGroup();
          m_AudioGroup.setMode(AudioGroup.MODE_NORMAL);
          m_AudioStream = new AudioStream(InetAddress.getByAddress(getLocalIPAddress ()));
          int localPort = m_AudioStream.getLocalPort();
          m_AudioStream.setCodec(AudioCodec.PCMU);
          m_AudioStream.setMode(RtpStream.MODE_NORMAL);

          ((TextView)findViewById(R.id.lblLocalPort)).setText(String.valueOf(localPort));

          ((Button) findViewById(R.id.button1)).setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                String remoteAddress = ((EditText)findViewById(R.id.editText2)).getText().toString();
                String remotePort = ((EditText)findViewById(R.id.editText1)).getText().toString();

                  try {
                    m_AudioStream.associate(InetAddress.getByName(remoteAddress), Integer.parseInt(remotePort));
                } catch (NumberFormatException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (UnknownHostException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                  m_AudioStream.join(m_AudioGroup);
            }
        });

          ((Button) findViewById(R.id.button2)).setOnClickListener(new OnClickListener() {

                @Override
                public void onClick(View v) {
                      m_AudioStream.release();
                }
            });

      } catch (Exception e) {
       Log.e("----------------------", e.toString());
       e.printStackTrace();
      }
}

public static byte[] getLocalIPAddress () {
    byte ip[]=null;
       try {
           for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) {
               NetworkInterface intf = en.nextElement();
               for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();) {
                   InetAddress inetAddress = enumIpAddr.nextElement();
                   if (!inetAddress.isLoopbackAddress()) {
                    ip= inetAddress.getAddress();
                   }
               }
           }
       } catch (SocketException ex) {
           Log.i("SocketException ", ex.toString());
       }
       return ip;
    }
}

布局文件:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/LinearLayout1"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

    <TextView
        android:id="@+id/lblLocalPort"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/localPort" />

    <EditText
        android:id="@+id/editText2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:ems="10"
        android:hint="@string/iPHint"
        android:inputType="phone" />

    <EditText
        android:id="@+id/editText1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:ems="10"
        android:hint="@string/portHint"
        android:inputType="number" >

        <requestFocus />
    </EditText>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content" 
        android:layout_marginTop="20dp">

        <Button
            android:id="@+id/button1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/connect" />

        <Button
            android:id="@+id/button2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/Disconnect" />
    </LinearLayout>
</LinearLayout>

编辑:IP地址方法在API 22上停止工作,请使用以下代码:

private byte[] getLocalIPAddress() {   
    byte[] bytes = null;

    try {
        // get the string ip
        WifiManager wm = (WifiManager) getSystemService(WIFI_SERVICE);
        String ip = Formatter.formatIpAddress(wm.getConnectionInfo().getIpAddress());

        // convert to bytes
        InetAddress inetAddress = null;
        try {
            inetAddress = InetAddress.getByName(ip);
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }

        bytes = new byte[0];
        if (inetAddress != null) {
            bytes = inetAddress.getAddress();
        }

    } catch (Exception e) {
        e.printStackTrace();
        Toast.makeText(this, R.string.phone_voip_incompatible, Toast.LENGTH_SHORT).show();
    }

    return bytes;
}

我遇到了这个异常 E/----------------------: java.net.SocketException: Invalid argument。 - Krishna Meena
我已经有一段时间没有使用它了,我知道在最近的Android更新中获取IP地址的方法已经改变。 - Murphybro2
你能指导我如何在一个安卓设备上通过本地WiFi向另一个设备进行语音通话吗? - Krishna Meena
现在它可以工作了,但是在按下连接按钮后没有任何反应。我该怎么做才能让它在按下连接按钮后连接到另一个设备并能够传输语音呢? - Krishna Meena
嗨Krishna,这是一个旧的解决方案,我不能保证它能在当前版本的Android上运行。当我第一次创建这篇文章时,你看到的代码就是使其工作所需的全部内容。如果它不再起作用,那么肯定有些东西已经改变了。 - Murphybro2
显示剩余7条评论

3

实际上,SIP客户端可以进行点对点通信,只需要知道彼此的IP地址和UDP端口,以便监听SIP消息。

您可以在两台计算机上使用普通的SIP客户端(如Windows上的X-Lite、Linux上的Twinkle等)进行测试,尝试在不进行服务器注册的情况下建立通话。这是完全可能的。

此外,您可以在本地局域网中的某个位置运行一个极简的SIP服务器。例如,FreeSWITCH可以被最小化到非常小的占用空间。


不,我不这么认为。它是用C语言编写的,而Android是基于Java的。 - Stanislav Sinyagin
Android基于LINUX C,Java是在C之上添加的。 - Costa Mirkin
是的,但您无法在未经root的手机上安装本地C应用程序。 - Stanislav Sinyagin
@StanislavSinyagin,您能否请看一下我的有关SIP的问题?https://dev59.com/BbTps4cB2Jgan1znVpGw - user2808671
我对Android编程不是很熟悉。你需要学习协议并尝试各种选项。 - Stanislav Sinyagin
显示剩余2条评论

2
我认为您可以使用JITSI在多个平台上进行点对点的VoIP服务,包括Andriod。
以下是我对该项目的发现:
  1. 不需要任何服务器或互联网连接。
  2. 用户必须处于同一网络下。
  3. 开源。
  4. Android apk可用,很可能你可以在网站上找到它的代码或者反编译它。

2

如果你正在寻找一些点对点通信,我认为wifi是最好的选择(更好的距离和速度)。如果你只想开发新版本的Android应用程序,那么Wi-Fi Direct是最好的选择,但这只适用于Android 4.0及以上版本。

如果你想在低于4.0的设备上运行应用程序,你需要使用第三方库。我知道高通有一个叫做AllJoyn的库,但不确定它的质量如何。


很好,但我需要VOIP应用程序 :) - Costa Mirkin
一旦你建立了连接,你就可以在其上运行任何你想要的服务。如果你正在寻找已经内置点对点能力的VoIP,那么我认为你不会找到它。 - MikeIsrael
实际上我需要点对多点的能力 :) - Costa Mirkin
@CostaMirkin,WiFi-Direct解决方案应该允许您拥有任意数量的连接。您是否阅读了链接另一端的资源? - MikeIsrael

1

这是不可能的,因为VOIP电话通过互联网和SIP服务器进行传递。

例如,如果您想通过VOIP拨号程序从您所在的国家拨打电话,那么您必须需要互联网接入,因为无法通过蓝牙通信。

谢谢。


但是如果我只想让两部手机使用WIFI相互通信,而不需要连接互联网呢? - Costa Mirkin
这是不可能的,因为你们需要一个服务器来进行通信。 - Md Abdul Gafur
仅适用于SIP。也许有一些方法可以在Java中构建VOIP应用程序而不使用SIP? - Costa Mirkin
1
VOIP可以在没有SIP的情况下构建。您只需要创建自己的语音数据传输协议即可。 - James Cross
请问您能推荐一些现有的协议吗? - Costa Mirkin
4
所有SIP用户代理都是服务器,这是根据定义的。在协议中并没有要求除SIP用户代理本身以外的任何内容。 - Frank Shearar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接