侧边栏
首页代码

Miracast技术详解(一):Wi-Fi Display

Miracast概述

Miracast

Miracast是由Wi-Fi联盟于2012年所制定,以Wi-Fi直连(Wi-Fi Direct)为基础的无线显示标准。支持此标准的消费性电子产品(又称3C设备)可透过无线方式分享视频画面,例如手机可透过Miracast将影片或照片直接在电视或其他设备播放而无需任何连接线,也不需透过无线热点(AP,Access Point)。

Wi-Fi Direct

Wi-Fi直连(英语:Wi-Fi Direct),之前曾被称为Wi-Fi点对点(Wi-Fi Peer-to-Peer),是一套无线网络互连协议,让wifi设备可以不必透过无线网络接入点(Access Point),以点对点的方式,直接与另一个wifi设备连线,进行高速数据传输。这个协议由Wi-Fi联盟发展、支持与授与认证,通过认证的产品将可获得Wi-Fi CERTIFIED Wi-Fi Direct®标志。

Wi-Fi Display

Wi-Fi Display是Wi-Fi联盟制定的一个标准协议,它结合了Wi-Fi标准和H.264视频编码技术。利用这种技术,消费者可以从一个移动设备将音视频内容实时镜像到大型屏幕,随时、随地、在各种设备之间可靠地传输和观看内容。

Miracast实际上就是Wi-Fi联盟对支持WiFi Display功能的设备的认证名称,产品通过认证后会打上Miracast标签。

Sink & Source

如下图所示,Miracast可分为发送端与接收端。Source端为Miracast音视频数据发送端,负责音视频数据的采集、编码及发送。而Sink端为Miracast业务的接收端,负责接收Source端的音视频码流并解码显示,其中通过Wi-Fi Direct技术进行连接。
image

Android上Wi-Fi Direct的实现

上面的概述里面也说到,Miracast是基于Wi-Fi Direct技术来实现连接与数据传输。那么要实现Miracast技术,首先就得研究下Android平台下的Wi-Fi Direct技术。

Wi-Fi P2P 简介

Wi-Fi Direct(在Android平台上也称Wi-Fi P2P),可以让具备相应硬件的Android 4.0(API 级别 14)或更高版本设备在没有AP的情况下,通过WLAN进行直接互联,使用这些 API,可以实现支持 WiFi P2P 的设备间相互发现和连接,从而获得比蓝牙连接更远距离的高速连接通信效果。

为了实现一个基础的WiFiP2P,大致分为如下部分:

  • 权限申请
  • 初始化WiFiP2P的相关对象
  • 定义监听WiFiP2P的广播接收器
  • 连接设备

关于WiFiP2P中的群组,大致分为如下部分:

  • 创建群组
  • 连接群组
  • 移除群组
  • 权限申请

权限申请

首先,在AndroidManifest.xml中,对WiFi相关权限进行静态申请:

<uses-sdk android:minSdkVersion="14" />
<!-- WiFi相关权限 -->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

若后续还需要读写权限则添加:

<!-- 读写权限 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

然后,在 Android 6.0 及更高版本中,部分危险权限(Dangerous Permissions)权限需要在运行时请求用户批准(动态申请):

private void checkPermission() {
    String[] permissions = new String[]{
            Manifest.permission.WRITE_EXTERNAL_STORAGE,
            Manifest.permission.READ_EXTERNAL_STORAGE,
            Manifest.permission.ACCESS_FINE_LOCATION
    };
    for (String permission : permissions) {
        if (ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED) {
            Log.i(TAG, permission + " granted.");
        } else {
            ActivityCompat.requestPermissions(this, permissions, 0);
            Log.w(TAG, permission + " not granted.");
        }
    }
}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    if (requestCode == 0) {
        for (int result : grantResults) {
            if (result == PackageManager.PERMISSION_GRANTED) {
                continue;
            } else {
                Toast.makeText(this, "权限未获取", Toast.LENGTH_SHORT).show();
            }
        }
    }
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}

初始化

首先,需要创建:

  • WifiP2pManager 对象
  • WifiP2pManager.Channel 对象
  • WiFiDirectBroadcastReceiver 对象(稍后介绍该广播接收器的定义)
  • IntentFilter 对象
private WifiP2pManager mManager;
private WifiP2pManager.Channel mChannel;
private WiFiDirectBroadcastReceiver mReceiver;
private IntentFilter mIntentFilter;

private void initWifip2pHelper() {
    // 创建 WifiP2pManager 对象
    mManager = (WifiP2pManager) getSystemService(WIFI_P2P_SERVICE);
    // 创建 WifiP2pManager.Channel 对象
    mChannel = mManager.initialize(this, Looper.getMainLooper(), new WifiP2pManager.ChannelListener() {
        @Override
        public void onChannelDisconnected() {
            Log.i(TAG, "onChannelDisconnected: ");
        }
    });
    // 创建 WiFiDirectBroadcastReceiver 对象
    mReceiver = new WiFiDirectBroadcastReceiver(mManager, mChannel, this);
    // 创建 IntentFilter 对象
    mIntentFilter = new IntentFilter();
    mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
    mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
    mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
    mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
    mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_DISCOVERY_CHANGED_ACTION);
}

其中, WiFiDirectBroadcastReceiver 对象想要监听的广播,与 IntentFilter 对象添加的action相同。

然后,在 Activity 的onResume()方法中注册广播接收器,在 Activity 的onPause()方法中取消注册该广播接收器:

/* register the broadcast receiver with the intent values to be matched */
@Override
protected void onResume() {
    super.onResume();
    registerReceiver(mReceiver, mIntentFilter);
}

/* unregister the broadcast receiver */
@Override
protected void onPause() {
    super.onPause();
    unregisterReceiver(mReceiver);
}

定义监听WiFiP2P的广播接收器

监听WiFiP2P的广播接收器 WiFiDirectBroadcastReceiver 类具体定义如下:

public class WiFiDirectBroadcastReceiver extends BroadcastReceiver {

    private static final String TAG = "WiFiDirectBroadcastReceiver";

    private WifiP2pManager mManager;
    private WifiP2pManager.Channel mChannel;
    private Wifip2pActivity mActivity;

    private List<WifiP2pDevice> mWifiP2pDeviceList = new ArrayList<>();

    WifiP2pManager.PeerListListener mPeerListListener = new WifiP2pManager.PeerListListener() {
        @Override
        public void onPeersAvailable(WifiP2pDeviceList wifiP2pDeviceList) {
            mWifiP2pDeviceList.clear();
            mWifiP2pDeviceList.addAll(wifiP2pDeviceList.getDeviceList());
        }
    };

    /**
     * 构造方法
     *
     * @param manager      WifiP2pManager对象
     * @param channel      WifiP2pManager.Channel对象
     * @param activity     Wifip2pActivity 对象
     */
    public WiFiDirectBroadcastReceiver(WifiP2pManager manager, WifiP2pManager.Channel channel, Wifip2pActivity activity) {
        super();
        this.mManager = manager;
        this.mChannel = channel;
        this.mActivity = activity;
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        switch (action) {
            case WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION:
                // Check to see if Wi-Fi is enabled and notify appropriate activity
                Log.i(TAG, "onReceive: WIFI_P2P_STATE_CHANGED_ACTION");
                int state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1);
                if (state == WifiP2pManager.WIFI_P2P_STATE_ENABLED) {
                    // Wifi P2P is enabled
                    Log.i(TAG, "onReceive: Wifi P2P is enabled");
                    mManager.discoverPeers(mChannel, new WifiP2pManager.ActionListener() {
                        @Override
                        public void onSuccess() {
                            Log.d(TAG, "onSuccess: ");
                        }

                        @Override
                        public void onFailure(int i) {
                            Log.d(TAG, "onFailure: ");
                        }
                    });
                } else {
                    // Wi-Fi P2P is not enabled
                    Log.i(TAG, "onReceive: Wi-Fi P2P is not enabled");
                }
                break;
            case WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION:
                // Call WifiP2pManager.requestPeers() to get a list of current peers
                Log.i(TAG, "onReceive: WIFI_P2P_PEERS_CHANGED_ACTION");
                if (mManager == null) {
                    return;
                }
                mManager.requestPeers(mChannel, mPeerListListener);
                break;
            case WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION:
                // Respond to new connection or disconnections
                Log.i(TAG, "onReceive: WIFI_P2P_CONNECTION_CHANGED_ACTION");
                // NetworkInfo
                NetworkInfo networkInfo = intent.getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO);
                // WifiP2pInfo
                WifiP2pInfo wifiP2pInfo = intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO);
                // WifiP2pGroup
                WifiP2pGroup wifiP2pGroup = intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_GROUP);
                if (networkInfo.isConnected()) {
                    if (wifiP2pInfo.isGroupOwner) {
                        Toast.makeText(mActivity, "设备连接,本设备为GO", Toast.LENGTH_LONG).show();
                    } else {
                        Toast.makeText(mActivity, "设备连接,本设备非GO", Toast.LENGTH_LONG).show();
                    }
                } else {
                    Toast.makeText(mActivity, "设备断开", Toast.LENGTH_LONG).show();
                }
                break;
            case WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION:
                // Respond to this device's wifi state changing
                Log.i(TAG, "onReceive: WIFI_P2P_THIS_DEVICE_CHANGED_ACTION");
                WifiP2pDevice device = intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_DEVICE);
                Log.d(TAG, "onReceive: " +device.deviceAddress);
                break;
            case WifiP2pManager.WIFI_P2P_DISCOVERY_CHANGED_ACTION:
                Log.i(TAG, "onReceive: WIFI_P2P_DISCOVERY_CHANGED_ACTION");
                break;
        }
    }
}

上述广播接收器用于监听系统关于WiFiP2P相关的广播。通常在onReceive()方法中,通过intent.getAction()方法获取到action,并根据action去匹配不同的关于WiFiP2P相关的广播,分别为:

  • WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION
  • WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION
  • WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION
  • WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION
  • WifiP2pManager.WIFI_P2P_DISCOVERY_CHANGED_ACTION

WIFI_P2P_STATE_CHANGED_ACTION:WiFiP2P状态发生改变时的广播

WiFiP2P具体有两个状态:

  • WifiP2pManager.WIFI_P2P_STATE_ENABLED:可用
  • WifiP2pManager.WIFI_P2P_STATE_DISABLED:不可用

而该状态的获取是由:

int state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1);

当WiFiP2P状态为可用时,调用discoverPeers()方法开始搜索附近WiFiP2P设备:

mManager.discoverPeers(mChannel, new WifiP2pManager.ActionListener() {
    @Override
    public void onSuccess() {
        Log.d(TAG, "onSuccess: ");
    }

    @Override
    public void onFailure(int i) {
        Log.d(TAG, "onFailure: ");
    }
});

WIFI_P2P_PEERS_CHANGED_ACTION:发现附近WiFiP2P设备时的广播

当搜索发现附近存在WiFiP2P设备时,调用requestPeers()方法开始获取附近WiFiP2P设备列表:

mManager.requestPeers(mChannel, mPeerListListener);

当成功获取附近WiFiP2P设备列表后,会回调侦听器 WifiP2pManager.PeerListListener 中的onPeersAvailable()方法,并传递一个 WifiP2pDeviceList 对象作为参数,可以用一个 List 对象接收并保存该参数:

WifiP2pManager.PeerListListener mPeerListListener = new WifiP2pManager.PeerListListener() {
    @Override
    public void onPeersAvailable(WifiP2pDeviceList wifiP2pDeviceList) {
        mWifiP2pDeviceList.clear();
        mWifiP2pDeviceList.addAll(wifiP2pDeviceList.getDeviceList());
    }
};

WIFI_P2P_CONNECTION_CHANGED_ACTION:连接状态发生改变时的广播

当连接状态发生改变时(如连接了一个设备,断开了一个设备),都会接收到该广播。当接收到该广播后,可以使用intent.getParcelableExtra()方法分别获取到 NetworkInfo , WifiP2pInfo , WifiP2pGroup 对象:

// 获取 NetworkInfo 对象
NetworkInfo networkInfo = intent.getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO);
// 获取 WifiP2pInfo 对象
WifiP2pInfo wifiP2pInfo = intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO);
// 获取 WifiP2pGroup 对象
WifiP2pGroup wifiP2pGroup = intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_GROUP);

获取到上述对象后,可以使用networkInfo.isConnected()方法来判断连接状态具体是“设备连接”还是“设备断开”,还可以根据wifiP2pInfo.isGroupOwner的值来判断设备是否为GroupOwner:

if (networkInfo.isConnected()) {
    if (wifiP2pInfo.isGroupOwner) {
        Toast.makeText(mActivity, "设备连接,本设备为GroupOwner", Toast.LENGTH_LONG).show();
    } else {
        Toast.makeText(mActivity, "设备连接,本设备非GroupOwner", Toast.LENGTH_LONG).show();
    }
} else {
    Toast.makeText(mActivity, "设备断开", Toast.LENGTH_LONG).show();
}

WIFI_P2P_THIS_DEVICE_CHANGED_ACTION:当前设备状态发生改变时的广播

通常可以在这个广播中获取到当前设备的信息:

WifiP2pDevice device = intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_DEVICE);

WifiP2pManager.WIFI_P2P_DISCOVERY_CHANGED_ACTION:搜索状态发生改变时的广播

启动搜索:

mManager.discoverPeers(mChannel, new WifiP2pManager.ActionListener() {
    @Override
    public void onSuccess() {
        Log.d(TAG, "onSuccess: ");
    }

    @Override
    public void onFailure(int i) {
        Log.d(TAG, "onFailure: ");
    }
});

停止搜索:

mManager.stopPeerDiscovery(mChannel, new WifiP2pManager.ActionListener() {
    @Override
    public void onSuccess() {
        Log.d(TAG, "onSuccess: ");
    }

    @Override
    public void onFailure(int i) {
        Log.d(TAG, "onFailure: ");
    }
});

连接设备

首先,选择一个需要连接的设备,并获取到该设备的 WifiP2pDevice 对象。然后,判断该设备的状态,设备状态通常为三种:

  • WifiP2pDevice.AVAILABLE:可连接
  • WifiP2pDevice.CONNECTED:已连接
  • WifiP2pDevice.INVITED:已请求连接

根据不同的设备状态,进行不同的具体逻辑:

@Override
public void onClick(View view) {
    // 获取到该设备的 WifiP2pDevice 对象
    WifiP2pDevice wifiP2pDevice = mWifiP2pDeviceList.get(viewHolder.getAdapterPosition());
    // 判断该设备的状态
    switch (wifiP2pDevice.status) {
        case WifiP2pDevice.AVAILABLE:
            // 请求连接
            WifiP2pConfig config = new WifiP2pConfig();
            config.deviceAddress = wifiP2pDevice.deviceAddress;
            mManager.connect(mChannel, config, new WifiP2pManager.ActionListener() {
                @Override
                public void onSuccess() {
                    Log.i(TAG, "connect success.");
                }

                @Override
                public void onFailure(int i) {
                    Log.i(TAG, "connect failed.");
                }
            });
            break;
        case WifiP2pDevice.CONNECTED:
            // 断开连接
            mManager.removeGroup(mChannel, new WifiP2pManager.ActionListener() {
                @Override
                public void onSuccess() {
                    Log.i(TAG, "removeGroup success.");
                }

                @Override
                public void onFailure(int i) {
                    Log.i(TAG, "removeGroup failed.");
                }
            });
            break;
        case WifiP2pDevice.INVITED:
            // 关闭连接请求
            mManager.cancelConnect(mChannel, new WifiP2pManager.ActionListener() {
                @Override
                public void onSuccess() {
                    Log.i(TAG, "cancelConnect success.");
                }

                @Override
                public void onFailure(int i) {
                    Log.i(TAG, "cancelConnect failed.");
                }
            });
            break;
    }
}

Wi-Fi P2P 连接

在发送端搜索到Miracast设备,并点击对应设备后,就进入到了连接过程。此时Sink端应该会弹出一个[连接邀请]的授权窗口,可以选择拒绝或者接受。选择接受后,若是第一次连接,则会进入到GO协商的过程。

GO协商(Group Owner Negotiation)

GO协商是一个复杂的过程,共包含三个类型的Action帧:GO Req、GO Resp、GO Confirm,经过这几个帧的交互最终确认是Sink端还是Source端作为Group Owner,因此谁做GO是不确定的。那具体的协商规则是怎样的呢?官方的流程图清晰地给出了答案:
image

首先通过Group Owner Intent的值进行协商,值大者为GO。若Intent值相同就需要判断Req帧中Tie breaker位,置1者为GO。若2台设备都设置了Intent为最大值,都希望能成为GO,则这次协商失败。

那么,如何设置这个Intent值呢?发送端在connect()的时候,可通过groupOwnerIntent字段设置GO的优先级的(范围从0-15,0表示最小优先级),方法如下:

WifiP2pConfig config = new WifiP2pConfig();
...
config.groupOwnerIntent = 15; // I want this device to become the owner
mManager.connect(mChannel, config, actionListener);

Miracast Sink端的场景为接收端,因此不能通过groupOwnerIntent字段来设置GO优先级。那么还有其他方式可以让Sink端成为GO吗?毕竟在多台设备通过Miracast投屏的时候,Sink端是必须作为GO才能实现的。答案其实也很简单,就是自己创建一个组,自己成为GO,让其他Client加进来,在连接前直接调用createGroup()方法即可完成建组操作:

mManager.createGroup(mChannel, new WifiP2pManager.ActionListener() {
    @Override
    public void onSuccess() {
        Log.d(TAG, "createGroup onSuccess");
    }

    @Override
    public void onFailure(int reason) {
        Log.d(TAG, "createGroup onFailure:" + reason);
    }
});

建组成功后我们可以通过requestGroupInfo()方法来查看组的基本信息,以及组内Client的情况:

mManager.requestGroupInfo(mChannel, wifiP2pGroup -> {
    Log.d(TAG, "onGroupInfoAvailable detail:\n" + wifiP2pGroup.toString());
    Collection<WifiP2pDevice> clientList = wifiP2pGroup.getClientList();
    if (clientList != null) {
        int size = clientList.size();
        Log.d(TAG, "onGroupInfoAvailable - client count:" + size);
        // Handle all p2p client devices
    }
});

GO协商完毕,并且Wi-Fi Direct连接成功的时候,我们将会收到WIFI_P2P_CONNECTION_CHANGED_ACTION这个广播,此时我们可以调用requestConnectionInfo(),并在onConnectionInfoAvailable()回调中通过isGroupOwner字段来判断当前设备是Group Owner,还是Peer。通过groupOwnerAddress,我们可以很方便的获取到Group Owner的IP地址。

@Override
public void onConnectionInfoAvailable(WifiP2pInfo wifiP2pInfo) {
    if (wifiP2pInfo.groupFormed && wifiP2pInfo.isGroupOwner) {
        Log.d(TAG, "is groupOwner: ");
    } else if (wifiP2pInfo.groupFormed) {
        Log.d(TAG, "is peer: ");
    }
    String ownerIP = wifiP2pInfo.groupOwnerAddress.getHostAddress();
    Log.d(TAG, "onConnectionInfoAvailable ownerIP = " + ownerIP);
}

受WiFi P2P API的限制,各设备获取到的MAC和IP地址情况如下图所示:
image
由于在后续RTSP进行指令通讯的时候,需要通过Socket与Source端建立连接,也就是我们需要先知道Source端的IP地址与端口。根据上图,我们可能出现以下2种情况:

  1. 情况1:Sink端为Peer,Source端为GO。
    这种情况下,Sink端知道Source端(GO)的IP地址,可以直接进行Socket连接。

  2. 情况2:Sink端为GO,Source端为Peer。
    这种情况下,Sink端只知道自己(GO)的IP地址,不知道Source端(Peer)的IP地址,但此时能获取到MAC地址。

通过ARP协议获取对应MAC设备的IP地址

针对上述情况2,我们需要通过MAC地址获取到对应主机的IP地址,以完成与Source端的Socket连接,比较经典的方案是采用解析ARP缓存表的形式进行。

ARP(Address Resolution Protocol),即地址解析协议,是根据IP地址获取物理地址的一个TCP/IP协议。主机发送信息时将包含目标IP地址的ARP请求广播到局域网络上的所有主机,并接收返回消息,以此确定目标的物理地址;收到返回消息后将该IP地址和物理地址存入本机ARP缓存中并保留一定时间,下次请求时直接查询ARP缓存以节约资源。

在Android上,我们可以通过以下指令获取ARP缓存表:

方法1:通过busybox arp指令

dior:/ $ busybox arp
? (192.168.0.108) at f8:ff:c2:10:e7:62 [ether]  on wlan0
? (192.168.0.1) at 9c:a6:15:d6:e8:f4 [ether]  on wlan0

方法2:通过cat proc/net/arp命令

dior:/ $ cat proc/net/arp
IP address       HW type     Flags       HW address            Mask     Device
192.168.0.108    0x1         0x2         f8:ff:c2:10:e7:62     *        wlan0
192.168.0.1      0x1         0x2         9c:a6:15:d6:e8:f4     *        wlan0

剩下的工作就是采用强大的正则表达式解析返回的字符串,并查找出对应MAC设备的IP地址了。

获取Source端RTSP端口号

经过上面的步骤,我们已经拿到了Source端的IP地址,只剩下端口号了。这一步就比较简单了,通过requestPeers()方法获取已连接的对等设备WifiP2pDevice,再获取其中的WifiP2pWfdInfo即可拿到端口号:

 mManager.requestPeers(mChannel, peers -> {
    Collection<WifiP2pDevice> devices = peers.getDeviceList();
    for (WifiP2pDevice device : devices) {
        boolean isConnected = (WifiP2pDevice.CONNECTED == device.status);
        if (isConnected) {
            int port = getDevicePort(device);
            break;
        }
    }
});

这里由于WifiP2pDevice中的wfdInfo字段为@hide,因此需要通过反射的方式获取WifiP2pWfdInfo。最后通过getControlPort()方法即可拿到Source端RTSP端口号:

public int getDevicePort(WifiP2pDevice device) {
    int port = WFD_DEFAULT_PORT;
    try {
        Field field = ReflectUtil.getPrivateField(device.getClass(), "wfdInfo");
        if (field == null) {
            return port;
        }
        WifiP2pWfdInfo wfdInfo = (WifiP2pWfdInfo) field.get(device);
        if (wfdInfo != null) {
            port = wfdInfo.getControlPort();
            if (port == 0) {
                Log.w(TAG,"set port to WFD_DEFAULT_PORT");
                port = WFD_DEFAULT_PORT;
            }
        }
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }
    return port;
}

拿到了Source端的IP地址与端口号后,我们就可以建立RTSP连接,建立后续控制指令的通道了,详见下篇博客。

参考文档

WLAN 直连(对等连接或 P2P)概览
通过 Wi-Fi 直连创建点对点连接
Android WiFi P2P开发实践笔记
wifi直连(Android)Wifi-Direct
「Android」WiFiP2P入门

posted @ 2022-07-13 14:51  咸鱼Jay  阅读(3235)  评论(0编辑  收藏  举报
页脚HTML代码