添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

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技术进行连接。

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是不确定的。那具体的协商规则是怎样的呢?官方的流程图清晰地给出了答案:

    首先通过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地址情况如下图所示:
    由于在后续RTSP进行指令通讯的时候,需要通过Socket与Source端建立连接,也就是我们需要先知道Source端的IP地址与端口。根据上图,我们可能出现以下2种情况:

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

    情况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入门