请尊重分享成果,转载请注明出处,本文来自,原文链接:
看到有网友在后台私信和询问录屏这部分推流相关的问题,感觉这篇博客早该写完了。事实上除了繁忙的工作加上春节假期一下子拖了近一个月之久。近期更新了Demo,加入了视频帧推流,需要的朋友可以看看Demo。
无论是音频还是视频编码,我们都需要原始的数据源,拿视频举例子,实际录屏直播也只是将屏幕的每一个视图间接的获取充当原始视频帧,和摄像头获取视频帧原理区别不大。如今Android设备几乎都已经满足硬编码的条件了(虽然有好有坏),所以我们假装曾经那些兼容性问题都不存在。
我们向服务器发送视频数据的时候,还需要先发送视频参数的数据给服务器以区分我们的视频源的格式、类型及FLV封装的一些关键信息(File Header / File TAG等如sps / pps等相关的meta data)给解码器。(PS: 关于FLV格式封装等视频编解码的分析推荐看看的相关博文)MediaCodec的细节可以自行查阅官方API。
并且推荐这些对我极为有用的文章资料,感谢这些作者的无私分享:
作者自行封装及实现的一个Android实施滤镜、RTMP推流的类库。代码结构需要花点时间理解和读懂,值得深入学习其中的实现,因为使用MediaProjection / VirtualDisplay 来进行录屏的话,官方并不提供帧率的控制,这需要用到OpenGL ES将VirtualDisplay中的surface进行绘制到MediaCodec中的surface。然而在这个库中作者已经实现了全部的操作,本篇文章也会围绕该库进行大致的分析。
在找到上面的类库之前,还是这位作者给出的思路才能够一点点往OpenGL ES这个坑里跳,越入越深,差点没爬出来。由于作者不方便放出源码,我只能通过他的描述一点点的实现。并且StackOverFlow中网友fadden也给出了相关的思路:
Google官方给的Demo,基本涵盖了OpenGL的各类用法,好好看看吧。
问题的缘由来自工作中某些需求所引起的,接下来我一一描述。
好不容易开发完成录屏直播,结果在低码率或者网络波动大的情况下,很多机型(尤其是小米)在60帧满帧的条件打出来的视频是那样的酸爽,动态的画面简直眼瞎。老板要求改帧率!降低帧率到30看看什么情况,最后实际选择使用了15FPS。
性能比起Bilibili还是要差一些,这里挖个坑,之后再填。
后期设定的方案是将推流放到remote service当中,该service为前台独立进程的service,对主进程的依赖性减弱一些(虽然APP在被杀死的时候也可能被杀死)
通过开启远程服务并与APP的进程进行进程间通信(IPC),寻求保活的方式花了一段时间,最后对MIUI的系统机制还是无果,Debug的时候发现MIUI拥有一个PowerKeeper,一旦触发就会对任何后台进程的APP(据说有白名单)进行KillApplication操作,在我的压力测试下,无一应用幸免(包括优化得极其稳定的Bilibili,GooglePlay录屏APP排行第一的AZ
之前阿里云搞活动,12块买了个1核2G / 1M 半年的服务器,正好一直闲置没用,为了完成这篇博客我也真是够拼的了,先按照上述链接搭建一个基于Nginx + RTMP协议的流媒体服务器。搭服务器的目的是为了完成推流的操作,毕竟不想用公司的资源来进行私人的活动。
很多朋友都问推流什么时候才有,那么今天我就完完整整的将录屏推流这块完善,Android客户端的Demo + 推流服务器的步骤实现,时间有限,只注重实现,代码质量之后重构。
丑话再说在前头,我本着一颗开源分享和学习的心来写博客和Demo,认为好的点赞、评论大家随意,但是本人能力和精力有限,这本属于一个Demo,如果认为太烂没参考价值,那么还请留点口德,默默关闭本页即可,有问题提出来我会回复并以改正,请求勿喷,谢谢~
大概将会包含几个部分:
可以看到实现录屏到本地的ScreenRecorder直接通过下面的方法进行音视频写入,而推流的话需要做以修改。
FLV的头文件信息发送给服务器后,就可以将我们的关键帧发送,注意流媒体服务器解析的时候首先要先得到第一帧关键帧才会开始解析后面的视频帧,所以我们还需要在编码器获取IDR帧的时候进行发送。MediaCodec的INFO_OUTPUT_FORMAT_CHANGED
这个状态可以获取sps / pps,再将数据处理包装后打到FLV的TAG中。
在librestreaming中Packager.java这个类主要就是做了上面这些事。仅两个方法实现,作者代码逻辑清晰易懂,这里就不多说了,再次感谢Lake哥!
可以看到音视频编码线程共用同一个RESFlvDataCollecter接口,负责监听编码线程的一举一动,从接口中将音视频编码帧送到同一个帧队列,发送线程取数据时取到什么就把数据喂给RtmpStreamingSender发送出去。
在librestreaming中使用了HandlerThread为WorkHandler提供Looper,通过Handler的消息队列循环机制来控制数据的发送,实际也可以自定义线程,手动管理视频帧收发队列,但涉及到了并发抢占资源的问题,更推荐Handler这种方式,并且Handler处理除了效率高、逻辑清晰,易管理之外还有一个好处,如果使用OpenGL绘制Surface时,正好可以Handler处理其中的异步操作。
不过在Demo中我修改为了一个普通的Runnable任务,run()
中循环处理frameQueue
中的数据。代码如下:
更多的请看Demo源码,说下目前未实现的功能和问题:
参照Nginx + rtmp搭建了流媒体服务器用来测试,Demo中键入以下地址便可推流,yourstramingkey自定义,
华为Mate采用6.1英寸全贴合IPS材质 HD高清大屏,拥有4050mAh锂聚合物电池,K3V2四核1.5GHz处理器,内嵌16核GPU,是高性能与低功耗的完美整合。
搭载基于Android 4.1系统的Emotion UI具有合一桌面、同屏多窗、单手操控、智能阅读、语音助手。
同时还具备史上最全视频解码能力,魔幻触控,双WIFI,双导航,通话降噪,信号增强,杜比音效等多项领先技术以及800万像素BSI背照式主摄像头。
华为总裁任正非,是军人出身,华为技术有限公司成立于1987年,当时老外垄断了大多数通讯设备市场,加上中国改革开放大背景,从华为名字由来诞生可以说是时代背景下的爱国创业。
华为从成立以来就保持积极进取的精神,通过持续的创新,向着国际化、职业化,实现自身的稳固成长。