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

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介: 技术心得:打造自己的智能投屏体验——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吗?可以的,就是乐播他们要收费,所以不做探究。


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


作者:白帽子耗子


链接:


来源:简书


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

相关文章
|
28天前
|
开发框架 前端开发 Android开发
安卓与iOS开发中的跨平台策略
在移动应用开发的战场上,安卓和iOS两大阵营各据一方。随着技术的演进,跨平台开发框架成为开发者的新宠,旨在实现一次编码、多平台部署的梦想。本文将探讨跨平台开发的优势与挑战,并分享实用的开发技巧,帮助开发者在安卓和iOS的世界中游刃有余。
|
1月前
|
缓存 前端开发 Android开发
安卓开发中的自定义视图:从零到英雄
【10月更文挑战第42天】 在安卓的世界里,自定义视图是一块画布,让开发者能够绘制出独一无二的界面体验。本文将带你走进自定义视图的大门,通过深入浅出的方式,让你从零基础到能够独立设计并实现复杂的自定义组件。我们将探索自定义视图的核心概念、实现步骤,以及如何优化你的视图以提高性能和兼容性。准备好了吗?让我们开始这段创造性的旅程吧!
26 1
|
1月前
|
搜索推荐 Android开发 开发者
探索安卓开发中的自定义视图:打造个性化UI组件
【10月更文挑战第39天】在安卓开发的世界中,自定义视图是实现独特界面设计的关键。本文将引导你理解自定义视图的概念、创建流程,以及如何通过它们增强应用的用户体验。我们将从基础出发,逐步深入,最终让你能够自信地设计和实现专属的UI组件。
|
15天前
|
搜索推荐 前端开发 API
探索安卓开发中的自定义视图:打造个性化用户界面
在安卓应用开发的广阔天地中,自定义视图是一块神奇的画布,让开发者能够突破标准控件的限制,绘制出独一无二的用户界面。本文将带你走进自定义视图的世界,从基础概念到实战技巧,逐步揭示如何在安卓平台上创建和运用自定义视图来提升用户体验。无论你是初学者还是有一定经验的开发者,这篇文章都将为你打开新的视野,让你的应用在众多同质化产品中脱颖而出。
40 19
|
28天前
|
IDE Java 开发工具
移动应用与系统:探索Android开发之旅
在这篇文章中,我们将深入探讨Android开发的各个方面,从基础知识到高级技术。我们将通过代码示例和案例分析,帮助读者更好地理解和掌握Android开发。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的信息和技巧。让我们一起开启Android开发的旅程吧!
|
16天前
|
JSON Java API
探索安卓开发:打造你的首个天气应用
在这篇技术指南中,我们将一起潜入安卓开发的海洋,学习如何从零开始构建一个简单的天气应用。通过这个实践项目,你将掌握安卓开发的核心概念、界面设计、网络编程以及数据解析等技能。无论你是初学者还是有一定基础的开发者,这篇文章都将为你提供一个清晰的路线图和实用的代码示例,帮助你在安卓开发的道路上迈出坚实的一步。让我们一起开始这段旅程,打造属于你自己的第一个安卓应用吧!
41 14
|
19天前
|
Java Linux 数据库
探索安卓开发:打造你的第一款应用
在数字时代的浪潮中,每个人都有机会成为创意的实现者。本文将带你走进安卓开发的奇妙世界,通过浅显易懂的语言和实际代码示例,引导你从零开始构建自己的第一款安卓应用。无论你是编程新手还是希望拓展技术的开发者,这篇文章都将为你打开一扇门,让你的创意和技术一起飞扬。
|
17天前
|
XML 存储 Java
探索安卓开发之旅:从新手到专家
在数字时代,掌握安卓应用开发技能是进入IT行业的关键。本文将引导读者从零基础开始,逐步深入安卓开发的世界,通过实际案例和代码示例,展示如何构建自己的第一个安卓应用。我们将探讨基本概念、开发工具设置、用户界面设计、数据处理以及发布应用的全过程。无论你是编程新手还是有一定基础的开发者,这篇文章都将为你提供宝贵的知识和技能,帮助你在安卓开发的道路上迈出坚实的步伐。
30 5
|
15天前
|
开发框架 Android开发 iOS开发
安卓与iOS开发中的跨平台策略:一次编码,多平台部署
在移动应用开发的广阔天地中,安卓和iOS两大阵营各占一方。随着技术的发展,跨平台开发框架应运而生,它们承诺着“一次编码,到处运行”的便捷。本文将深入探讨跨平台开发的现状、挑战以及未来趋势,同时通过代码示例揭示跨平台工具的实际运用。
|
17天前
|
XML 搜索推荐 前端开发
安卓开发中的自定义视图:打造个性化UI组件
在安卓应用开发中,自定义视图是一种强大的工具,它允许开发者创造独一无二的用户界面元素,从而提升应用的外观和用户体验。本文将通过一个简单的自定义视图示例,引导你了解如何在安卓项目中实现自定义组件,并探讨其背后的技术原理。我们将从基础的View类讲起,逐步深入到绘图、事件处理以及性能优化等方面。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的见解和技巧。
下一篇
DataWorks