技术心得:打造自己的智能投屏体验——Android投屏开发入门

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 技术心得:打造自己的智能投屏体验——Android投屏开发入门

前言


目前音视频领域的应用已涉及到众多领域,而其中投屏功能,成为很多家庭影院、会议观看等的高频使用场景。为了尽享大屏体验,不妨自己来实现一个投屏功能


1、投屏原理简介


投屏,就是将一台设备上的媒体内容,通过一定的技术方案,在另外一台设备上显示。其中这个一定的技术方案,主要分为两种:


推送模式:主要用于投屏音视频。投屏之后手机可以关闭,电视(接收端)不会停止播放。核心原理就是通过指定协议,类似于蓝牙那样搜索匹配,并将音视频的播放地址传输过去,然后接收端播放这个地址的流媒体。常见的投屏协议有DLNA、Airplay。


镜像模式:所谓的镜像就是同屏,把手机(发送端)的屏幕内容同步传输到电视(接收端)上显示。核心原理就是一边录屏一遍发送给电视同步播放。常见协议有Miracast、Airplay。


一般来说我们常用的投屏是推送模式,本文主要也是介绍这个。


1.1 投屏协议——DLNA


DLNA代表“数字生活网络联盟”。DLNA使用通用即插即用(UPnP)协议。DLNA并不是真正的无线显示解决方案。相反,它只是一种在一个设备上获取内容并在另一台设备上播放内容的方法。也就是说他不是真正的投屏技术。


我们手机上爱奇艺APP、腾讯视频APP,在打开视频后,右上角有一个【TV】的小图标,你点击这个小图标,就会弹出“正在搜寻可投屏设备”,将会显示同一个Wi-Fi网络下能够发现的投屏设备,选择投屏的电视机后,电视机就会播放对应的视频。这里有一个注意点,就是当你在手机上是VIP会员时,你要想将VIP视频通过DLNA投屏到智能电视上时,是没法投屏的,因为爱奇艺或者腾讯将限制这种操作,避免手机VIP用户通过投屏来实现电视机播放VIP视频,原因就是DLNA协议要求最终还是需要智能电视自己去视频服务器获取视频,视频服务器可能会在流媒体地址的获取/解析等做限制。


1.2 投屏协议 —— Miracast


Miracast是Wi-Fi联盟制定的Wi-Fi投屏行业标准,实质上是对Apple AirPlay的回应。Miracast支持内置在Android 4.2+和Windows 8.1、Windows 10。允许Android智能手机、Windows平板电脑和笔记本电脑以及其他设备以无线方式传输到兼容Miracast的接收器比如智能电视、平板电脑等。当前已经有很多电视盒子都支持Miracast协议,比如小米盒子、荣耀盒子等等,小米手机、华为的手机也都支持Miracast协议,配合小米盒子、荣耀盒子即可实现投屏。


各品牌设备该功能名称可能不同,比如:无线显示、屏幕共享、多屏互动、Screen Mirroring等。可以看这个乐播关于设备的入口收集部分:


Miracast相比AirPlay来讲,有缺点也有优点,优点在于:


内置在Andorid和Windows中,不要求必须是苹果的终端设备。


Miracast可以在没有无线路由器的时候也能很好的工作,也就是说手机可以直接通过Wi-Fi连接到电视的Wi-Fi网卡上进行投屏(Wi-Fi Direct技术),在没有无线路由器的时候是比较方便的。


缺点在于:


只支持屏幕镜像模式投屏,而不支持流模式的投屏。当你在投屏的时候手机整个屏幕(包括状态栏等)会复制到电视机上,并且要始终保持手机屏幕是处于播放和显示状态。苹果的AirPlay则可以允许你在手机上一?边浏览网页,一边通过电视播放手机中的视频。


Miracast毕竟是一种行业标准,各个厂家实现良莠不齐,不同设备之间投屏可能出现体验不佳的问题。


另一个问题是该标准不要求设备必须带有“ Miracast”品牌的商标。制造商已将其Miracast实现称为其他东西。例如,LG称其Miracast支持为“ SmartShare”,三星称其为“ AllShare Cast”,索尼称其为“屏幕镜像”,而松下称其为“显示镜像”。


1.3 投屏协议 ——Airplay


AIrplay协议是苹果的协议,主要局限在仅适用于 Apple 设备,我们在这里不做展开。


1.4 投屏协议 —— 其他第三方


最后就是很多专门投屏的投屏APP,这些APP要么是实现了上面几种协议,要么是自己实现一套私有协议。手机和智能电视都要安装这些APP,否则无法投屏。而前面几个协议都是标准协议,操作系统内置,无需安装。比较著名的投屏APP有乐播投屏、APowerMirror等,使用都很方便,一般是通过扫描智能电视显示的二维码来实现投屏到特定电视机上。这些投屏APP的另外一个好处就是:不局限在同一个局域网内,可以跨三层网络、甚至广域网。


2. 投屏功能开发实践


在这里我们选择用来保利威的官方Demo为例,之所以用它为例,是因为他也是一家视频提供商,并且提供了视频加密服务,也就是说,他可以做到提供主流视频厂商那样的VIP视频服务,并且其允许投屏。我们可以查看官方文档,借此探究Android投屏的开发实现。其基本都封装好了,我们可以复制过来改改就能应用到自己项目上,也可以参考实现。


2.1 投屏环境搭建和体验


1、注册第三方投屏SDK(可选)


第三方SDK往往和电视厂家有一定的合作,会内置支持,或者提供对应的电视端APP,可以拥有更良好的投屏体验。如果要自己实现投屏的话,还需要对实现协议对接,甚至还要开发对应的接收端APP,工作量上就大了不少。由于保利威的demo投屏功能是基于乐播的,如果需要集成到自己项目上,就需要在乐播上注册绑定包名生成key。当然我们直接运行demo,里面就内置了对应的key,体验的话可以忽略这一步。


2、 准备两台Android手机


因为开发者未必有电视,可以用另外一台Android设备充当电视接收端。接收端需要安装乐播的apk,乐播apk在安卓应用市场就能找到,如果应用市场没有,也可以去乐播官网进行下载乐播投屏电视版。


3、下载Demo工程


本文是基于Github Demo项目讲解,所以可以直接下载他们的Github项目运行体验。下载地址:


Demo中默认隐藏了投屏按钮,如果要体验的话,需要在PolyvPlayerActivity中取消注释


//投屏功能默认隐藏,如果需要请注释下面两行代码


// iv_screencast_search.setVisibility(View.GONE);


// iv_screencast_search_land.setVisibility(View.GONE);


然后,我们将两台手机(发送端和接收端),分别打开对应的APP,将其置于同一个wifi(局域网)之下,就可以开始投屏了。


2.2 投屏开发浅析


我们可以看下Demo结构中的投屏模块,其中widget是关于UI层的实现,主要是PolyvScreencastManager,封装了投屏功能。我们主要就是聚焦这个的实现。


//com/easefun/polyvsdk/cast


├── PolyvAllCast.java //乐播投屏二次封装类


├── PolyvIUIUpdateListener.java // 封装的投屏状态回调监听器


├── PolyvScreencastManager.java //投屏封装工具类,操作投屏功能使用。等同于旧版的PolyvScreencastHelper


└── widget


├── PolyvScreencastSearchLayout.java //搜索投屏Layout


└── PolyvScreencastStatusLayout.java //投屏状态管理Layout


2.2.1 初始化


从官方文档中可以知道初始化要设置AppSecret。这是乐播提供的服务中,把投屏sdk与包名绑定了,如果更换了包名我们就要重新注册,否则包名错误就会导致校验失败。然后会因此无法搜索到设备。


//appId和appSecret需与包名绑定,获取方式请到乐播官网注册获取


PolyvScreencastManager.init(, );


//初始化单例


PolyvScreencastManager.getInstance(this);


//PolyvAllCast.java


private void initLelinkService(Context context, String appid, String appSecret) {


LelinkSourceSDK.getInstance()


.setBindSdkListener(new IBindSdkListener() {


@Override


//代码效果参考:http://hnjlyzjd.com/hw/wz_24846.html

public void onBindCallback(boolean result) {

//绑定,并且返回绑定的结果


Log.e(TAG, "Polyv Cast SDK Init Result :" + result);


if (result) {


LelinkSourceSDK.getInstance().setOption(IAPI.OPTION_5, false);


LelinkSourceSDK.getInstance().setDebugMode(true);


LelinkSourceSDK.getInstance().enableLogCache(true);


}


}


})


.setSdkInitInfo(context, appid, appSecret)


.bindSdk();


}


我们可以看见在PolyvAllCast中进行了投屏SDK的初始化与绑定服务,并且返回了结果。这种绑定服务往往依赖于网络,最好就在Application中就进行初始化,避免网络延迟导致投屏服务异常。


2.2.2 三大回调监听


如果有蓝牙开发经验的,其实可以很容易理解,这和蓝牙的搜索匹配以及数据传输流程,基本一致。投屏模块的核心实现在于把握投屏的最基本的几个流程:初始化 → 搜索设备 →(返回设备列表)→ 连接设备 → (返回连接监听) → 开始投屏 → (返回投屏状态回调) → 停止投屏 → 断开连接。


初始化在前面已经提到过了,然后就是基本的三大回调:


搜索设备回调


连接状态回调


投屏状态回调


在PolyvScreencastManager中已经实现三大回调监听,然后经过转发到PolyvIUIUpdateListener,把这三大回调状态分割成不同的状态码,回调到上层。


public interface PolyvIUIUpdateListener {


int STATE_SEARCH_SUCCESS = 1;//搜索成功


int STATE_SEARCH_ERROR = 2;//搜索失败


int STATE_SEARCH_NO_RESULT = 3;//搜索设备无结果


int STATE_CONNECT_SUCCESS = 10;//链接成功


int STATE_DISCONNECT = 11;// 连接断开


int STATE_CONNECT_FAILURE = 12;// 连接失败


//下面是投屏状态,如播放暂停完成等


int STATE_PLAY = 20;


int STATE_PAUSE = 21;


int STATE_COMPLETION = 22;


int STATE_STOP = 23;


int STATE_SEEK = 24;


int STATE_POSITION_UPDATE = 25;


int STATE_PLAY_ERROR = 26;


int STATE_LOADING = 27;


int STATE_INPUT_SCREENCODE = 28;


int RELEVANCE_DATA_UNSUPPORT = 29;


void onUpdateState(int state, Object object);


void onUpdateText(String msg);


}


在调用screencastManager.browse/stopBrowse ()时候就会开始/停止搜索。我们可以看一下搜索设备回调,返回了 resultCode,是里面解析了各个 resultCode 对应的含义,并将其通过 handler 回调到主线程。包括IConnectListener、ILelinkPlayerListener 也是如此,这两个就不作展开。


//PolyvScreencastManager.java


/


投屏搜索监听


/


private IBrowseListener mBrowseListener = new IBrowseListener() {


@Override


public void onBrowse(int resultCode, List list) {


PolyvCommonLog.d(TAG, "onSuccess size:" + (list == null ? 0 : list.size()));


mInfos = list;


if (resultCode == IBrowseListener.BROWSE_SUCCESS) {


//...


if (null != mUIHandler) {


// 发送文本信息


mUIHandler.sendMessage(buildTextMessage(buffer.toString()));


if (mInfos.isEmpty()) {


mUIHandler.sendMessage(buildStateMessage(PolyvIUIUpdateListener.STATE_SEARCH_NO_RESULT));


} else {


mUIHandler.sendMessage(buildStateMessage(PolyvIUIUpdateListener.STATE_SEARCH_SUCCESS));


}


}


}


} else {


if(resultCode == IBrowseListener.BROWSE_STOP){


return;


}


if (null != mUIHandler) {


// 发送文本信息


PolyvCommonLog.d(TAG, "browse error:Auth error");


String text = "";


if(resultCode == IBrowseListener.BROWSE_ERROR_AUTH){


text = "授权失败";


} else if(resultCode == IBrowseListener.BROWSE_ERROR_AUTH_TIME){


text = "授权失败次数超限";


} else {


text = "搜索错误";


}


mUIHandler.sendMessage(buildTextMessage(text));


mUIHandler.sendMessage(buildStateMessage(PolyvIUIUpdateListener.STATE_SEARCH_ERROR));


}


}


}


};


/


投屏连接状态监听


/


private IConnectListener mConnectListener = new IConnectListener() {


@Override


public void onConnect(final LelinkServiceInfo serviceInfo, final int extra) {


}


@Override


public void onDisconnect(LelinkServiceInfo serviceInfo, int what, int extra) {


}


};


/*


投屏播放监听


*/


private ILelinkPlayerListener mPlayerListener = new ILelinkPlayerListener() {


@Override


public void onLoading() {


}


@Override


public void onStart() {


}


@Override


public void onPause() {


}


//。。。省略


};


2.2.3 视频播放


播放视频投屏主要是通过playNetMedia方法。其中通过setType和setUrl设置流媒体的类型和流媒体地址。从下面的代码中就可以看出其投屏的API,实际上是推送模式,把流媒体地址传输到接收端去解析播放的。


//PolyvScreencastManager.java


public void playNetMedia(LelinkPlayerInfo lelinkPlayerInfo, String playPath, int type, String screenCode, int seconds) {


currentPlayPath = playPath;


lelinkPlayerInfo.setType(type);


lelinkPlayerInfo.setUrl(playPath);


String userAgent = "PolyvAndroidScreencast-lelink" + BuildConfig.VERSION_NAME;


lelinkPlayerInfo.setHeader("{\"user-agent\":\" " + userAgent + "\"}");


lelinkPlayerInfo.setLoopMode(LelinkPlayerInfo.LOOP_MODE_DEFAULT);


lelinkPlayerInfo.setOption(IAPI.OPTION_6, screenCode);


lelinkPlayerInfo.setStartPosition(seconds);


mAllCast.playNetMediaWithHeader(lelinkPlayerInfo);


}


在Demo中,他还设置了Header,一般可以通过这个来设置参数,如user-agent等,可以借此跟踪发送端设备参数。但是这个LelinkPlayerInfo 是乐播提供的实体,这种Header的设置,只能在乐联协议中生效!而在DLNA连接中其实是无法生效的。更好的解决方案,是通过playPath中追加参数。


乐联协议:也就是上面说的第三方投屏协议,这是乐播自己修改兼容的投屏协议。


以上基本就是PolyvScreencastManager提供的投屏封装的功能了,关于基本使用可以查看文档。


3. 加密视频投屏


前面有说到,很多视频站都有VIP视频,这些视频往往投屏之后也不支持解析播放。那保利威中的加密视频是如何做到投屏播放的呢?


我们可以追查到PolyvScreencastSearchLayout#loadInfoAndPlay方法,它通过一个PolyvScreencastHelper.getInstance().transformPlayObject()方法,将LelinkPlayerInfo对象转化成为了支持解密的对象。


//PolyvScreencastSearchLayout#loadInfoAndPlay


LelinkPlayerInfo lelinkPlayerInfo = new LelinkPlayerInfo();


PolyvScreencastHelper.getInstance().transformPlayObject(lelinkPlayerInfo, video,


bitrate, playPath, new PolyvScreencastHelper.PolyvCastTransformCallback() {


@Override


public void onSucceed(Object object, String newPlayPath) {


PolyvCommonLog.d(TAG, "cast: " + newPlayPath);


int videoPosition = screencastStatusLayout.getCurrentPlayPosition();


play((LelinkPlayerInfo) object, newPlayPath, bitrate, videoPosition);


screencastStatusLayout.resetBitRateView(bitrate);


}


@Override


public void onFailed(Throwable e) {


}


});


}


});


无疑这个也是通过乐联协议去匹配的。乐播的文档中也提供了接口


lelinkPlayerInfo.setAesKey(String key)


lelinkPlayerInfo.setAesIv(String iv)


那么一个加密视频的播放,往往就需要在发送端和接收端都要去做一定的兼容,或者直接使用这种第三方SDK提供接口,通过约定的加解密协议方式,去对视频进行解密,然后再渲染播放。


4. 扩展:WifiDisplay介绍


前面说到的投屏分为Miracast和DLNA。上面一直说的都是DLNA的推送模式,那么关于镜像模式又是如何实现的呢?


镜像投屏,核心技术原理和WifiDisplay有关。它的本质和DLNA其实相似,都是建立连接然后传输数据,只是这里的数据是屏幕数据,数据量和交互都比较大,所以形成了一套协议方案。


WifiDIsplay涉及的技术和协议比较多,包括了WIFI P2P技术、RTSP及RTP技术、流媒体技术以及音视频编解码相关的技术等等。


我们可以通过 Wi-Fi 直连创建点对点连接,然后在这个基础上从传输文件到传输音视频。大致的原理就是将Source端(发送端)的音视频数据投屏到Sink端(接收端)。


以上可以简单拆分为几个过程:


1、设备(Source/Sink端)发现彼此:实际上属于P2P Device的彼此发现(涉及到WifiP2pManager.discoverPeers + (WifiP2pService) )


2、发起connect:WFD支持能力的协商,如双方设备支持的分辨率,还有会话的建立(WFD Capalibility Negotiation + WFD Session Establish)


3、Source端的Vedio/Audio 的capture 以及 encoder:(涉及到 Vedio Driver + Audio driver + Audio Capture)


4、上面的音视频数据从Soure端到Sink端。


看似做起来有点麻烦,那么能使用第三方的SDK吗?可以的,就是乐播他们要收费,所以不做探究。


-----------------------------------------------------------------------------------------------------------------------------------------------------------


作者:白帽子耗子


链接:


来源:简书


著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

相关文章
|
7天前
|
编解码 Java Android开发
通义灵码:在安卓开发中提升工作效率的真实应用案例
本文介绍了通义灵码在安卓开发中的应用。作为一名97年的聋人开发者,我在2024年Google Gemma竞赛中获得了冠军,拿下了很多项目竞赛奖励,通义灵码成为我的得力助手。文章详细展示了如何安装通义灵码插件,并通过多个实例说明其在适配国际语言、多种分辨率、业务逻辑开发和编程语言转换等方面的应用,显著提高了开发效率和准确性。
|
6天前
|
Android开发 开发者 UED
安卓开发中自定义View的实现与性能优化
【10月更文挑战第28天】在安卓开发领域,自定义View是提升应用界面独特性和用户体验的重要手段。本文将深入探讨如何高效地创建和管理自定义View,以及如何通过代码和性能调优来确保流畅的交互体验。我们将一起学习自定义View的生命周期、绘图基础和事件处理,进而探索内存和布局优化技巧,最终实现既美观又高效的安卓界面。
19 5
|
4天前
|
JSON Java Android开发
探索安卓开发之旅:打造你的第一个天气应用
【10月更文挑战第30天】在这个数字时代,掌握移动应用开发技能无疑是进入IT行业的敲门砖。本文将引导你开启安卓开发的奇妙之旅,通过构建一个简易的天气应用来实践你的编程技能。无论你是初学者还是有一定经验的开发者,这篇文章都将成为你宝贵的学习资源。我们将一步步地深入到安卓开发的世界中,从搭建开发环境到实现核心功能,每个环节都充满了发现和创造的乐趣。让我们开始吧,一起在代码的海洋中航行!
|
6天前
|
缓存 数据库 Android开发
安卓开发中的性能优化技巧
【10月更文挑战第29天】在移动应用的海洋中,性能是船只能否破浪前行的关键。本文将深入探讨安卓开发中的性能优化策略,从代码层面到系统层面,揭示如何让应用运行得更快、更流畅。我们将以实际案例和最佳实践为灯塔,引领开发者避开性能瓶颈的暗礁。
17 3
|
7天前
|
安全 搜索推荐 Android开发
揭秘安卓与iOS系统的差异:技术深度对比
【10月更文挑战第27天】 本文深入探讨了安卓(Android)与iOS两大移动操作系统的技术特点和用户体验差异。通过对比两者的系统架构、应用生态、用户界面、安全性等方面,揭示了为何这两种系统能够在市场中各占一席之地,并为用户提供不同的选择。文章旨在为读者提供一个全面的视角,理解两种系统的优势与局限,从而更好地根据自己的需求做出选择。
20 2
|
8天前
|
存储 IDE 开发工具
探索Android开发之旅:从新手到专家
【10月更文挑战第26天】在这篇文章中,我们将一起踏上一段激动人心的旅程,探索如何在Android平台上从零开始,最终成为一名熟练的开发者。通过简单易懂的语言和实际代码示例,本文将引导你了解Android开发的基础知识、关键概念以及如何实现一个基本的应用程序。无论你是编程新手还是希望扩展你的技术栈,这篇文章都将为你提供价值和启发。让我们开始吧!
|
8天前
|
安全 搜索推荐 Android开发
揭秘iOS与安卓系统的差异:一场技术与哲学的较量
在智能手机的世界里,iOS和Android无疑是两大巨头,它们不仅定义了操作系统的标准,也深刻影响了全球数亿用户的日常生活。本文旨在探讨这两个平台在设计理念、用户体验、生态系统及安全性等方面的本质区别,揭示它们背后的技术哲学和市场策略。通过对比分析,我们将发现,选择iOS或Android,不仅仅是选择一个操作系统,更是选择了一种生活方式和技术信仰。
|
13天前
|
设计模式 IDE Java
探索安卓开发:从新手到专家的旅程
【10月更文挑战第22天】 在数字时代的浪潮中,移动应用开发如同一座金矿,吸引着无数探险者。本文将作为你的指南针,指引你进入安卓开发的广阔天地。我们将一起揭开安卓平台的神秘面纱,从搭建开发环境到掌握核心概念,再到深入理解安卓架构。无论你是初涉编程的新手,还是渴望进阶的开发者,这段旅程都将为你带来宝贵的知识和经验的财富。让我们开始吧!
|
14天前
|
安全 Android开发 iOS开发
iOS与安卓:技术生态的双雄争霸
在当今数字化时代,智能手机操作系统的竞争愈发激烈。iOS和安卓作为两大主流平台,各自拥有独特的技术优势和市场地位。本文将从技术架构、用户体验、安全性以及开发者支持四个方面,深入探讨iOS与安卓之间的差异,并分析它们如何塑造了今天的移动技术生态。无论是追求极致体验的苹果用户,还是享受开放自由的安卓粉丝,了解这两大系统的内在逻辑对于把握未来趋势至关重要。
|
3天前
|
移动开发 Java Android开发
探索Android与iOS开发的差异性与互联性
【10月更文挑战第32天】在移动开发的大潮中,Android和iOS两大平台各领风骚。本文将深入浅出地探讨这两个平台的开发差异,并通过实际代码示例,展示如何在各自平台上实现相似的功能。我们将从开发环境、编程语言、用户界面设计、性能优化等多个角度进行对比分析,旨在为开发者提供跨平台开发的实用指南。
20 0