EasyPusher实现Android手机屏幕桌面直播,实时推送操作画面,用于手游直播等应用
分类:
本文转自EasyDarwin开源团队成员John的博客:http://blog.csdn.net/jyt0551/article/details/52651194
由于Android 5.0提供了捕获当前屏幕画面的SDK,基于该SDK,EasyPusher实现了实时推送手机屏幕画面的功能。经测试,效果很不错,延迟也比较低,画面也比较流畅。该功能可运用到小型会议PPT演示、手游直播等行业。
具体来说,MediaProjection 类可以将当前屏幕画面采集到一个surface里面,而MediaCodec可以从一个surface里面获取视频数据源。我们让MediaProjection投射到MediaCodec创建的Surface,MediaCodec就可以获取到MediaProjection投射的视频了。如图所示:
MediaProjectionMediaProjectionSurfaceSurfaceMediaCodecMediaCodecEasyPusherEasyPusher投射屏幕提供视频源编码编码数据通过Pusher推送
在这里就不再详细描述代码的实现,主要介绍下两个接口:
<code class="hljs applescript has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">VirtualDisplay createVirtualDisplay (String <span class="hljs-property" style="box-sizing: border-box;">name</span>, int width, int height, int dpi, int flags, Surface surface, VirtualDisplay.Callback callback, Handler handler) Creates a VirtualDisplay <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">to</span> capture <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">the</span> <span class="hljs-property" style="box-sizing: border-box;">contents</span> <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">of</span> <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">the</span> screen. Parameters <span class="hljs-property" style="box-sizing: border-box;">name</span> String: The <span class="hljs-property" style="box-sizing: border-box;">name</span> <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">of</span> <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">the</span> virtual display, must be non-empty. 要创建的投射器的名称,非空 width int: The width <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">of</span> <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">the</span> virtual display <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">in</span> pixels. Must be <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">greater than</span> <span class="hljs-number" style="color:#06666;box-sizing: border-box;">0.</span> 投射后视频的宽度,这里的宽度就是实际上后面MediaCodec初始化的宽度. height int: The height <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">of</span> <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">the</span> virtual display <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">in</span> pixels. Must be <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">greater than</span> <span class="hljs-number" style="color:#06666;box-sizing: border-box;">0.</span> 投射后视频的高度,这里的宽度就是实际上后面MediaCodec初始化的高度. dpi int: The density <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">of</span> <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">the</span> virtual display <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">in</span> dpi. Must be <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">greater than</span> <span class="hljs-number" style="color:#06666;box-sizing: border-box;">0.</span> 投射器的像素密度,未理解啥意思,我们直接用DisplayMetrics的densityDpi即可. flags int: A combination <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">of</span> virtual display flags. See DisplayManager <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">for</span> <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">the</span> full <span class="hljs-type" style="box-sizing: border-box;">list</span> <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">of</span> flags. 我们传 DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR|DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC|DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION 即可。 surface Surface: The surface <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">to</span> which <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">the</span> content <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">of</span> <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">the</span> virtual display should be rendered, <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">or</span> null <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">if</span> there <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">is</span> none initially. 投射器要投射到的Surface callback VirtualDisplay.Callback: Callback <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">to</span> call when <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">the</span> virtual display's state changes, <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">or</span> null <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">if</span> none. 投射器更改后的状态回调,我们这里不需要,传null即可。 handler Handler: The Handler <span class="hljs-function_start" style="box-sizing: border-box;"><span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">on</span></span> which <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">the</span> callback should be invoked, <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">or</span> null <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">if</span> <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">the</span> callback should be invoked <span class="hljs-function_start" style="box-sizing: border-box;"><span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">on</span></span> <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">the</span> calling thread's main Looper. 回调函数将在该Handler所在的线程调用,我们也不需要,传null即可。</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li></ul>
<code class="hljs livecodeserver has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">createInputSurface Surface createInputSurface () Requests <span class="hljs-operator" style="box-sizing: border-box;">a</span> Surface <span class="hljs-built_in" style="color:#66066;box-sizing: border-box;">to</span> use <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">as</span> <span class="hljs-operator" style="box-sizing: border-box;">the</span> input <span class="hljs-built_in" style="color:#66066;box-sizing: border-box;">to</span> <span class="hljs-operator" style="box-sizing: border-box;">an</span> encoder, <span class="hljs-operator" style="box-sizing: border-box;">in</span> place <span class="hljs-operator" style="box-sizing: border-box;">of</span> input buffers. 该接口创建一个作为编码器输入的Surface。</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li></ul>
然后,将后者得到的Surface作为第6个参数传给前者,前者就可以获取到屏幕数据了~
如下图所示,在VLC访问RTSP地址,即可看到屏幕直播。
EasyPusher项目见:https://github.com/EasyDarwin/EasyPusher