前言
通过我的上一篇文章,可以知道直播大致有几个步骤:音视频采集 -> 美颜/滤镜/特效处理 -> 编码 -> 封包 -> 推流 -> 分发 -> 解码/渲染/播放。那么首先便从采集开始,这里我先做的视频采集。
那么实时采集视频有哪些方案呢?
调研
通过各种调研,查阅文章,了解到目前Android实时采集视频大致有3种方式:
- 通过Android Camera拍摄预览中设置setPreviewCallback实现onPreviewFrame接口,实时截取每一帧视频流数据
- 通过通过Android的MediaRecorder,在SetoutputFile函数中绑定LocalSocket实现
- 流媒体服务器方式,利用ffmpeg或GetStreamer等获取Camera视频
通过学习,大致了解了1,2两种方式的实现方式,但是对于第3种方式,暂时没有研究。
spydroid
当我们在接触一个全新领域的时候,最希望的是能实实在在看到一个demo产品,通过demo产品我们更容易理解其内在的原理。在网上看到了许多的开源项目,最后选择了spydroid,感觉它跟Android结合更紧密。更多的信息可以参考Android视频采集方案总结。
拷贝工程
通过github看到的项目是Eclipse结构,这里我把代码拷贝下来后,通过AS打开,配置一些信息后项目结构如下:
streaming是作者封装的一套库。
A solution for streaming H.264, H.263, AMR, AAC using RTP on Android
运行
项目拷贝到AS后,有部分错误,修复后成功运行在MI 4LTE。
它可以通过http,也可以通过rtsp进行推流,打开rtsp推流的开关,首页会多了一个VLC的地址。
我在Win 10上使用Chrome接收失败,打开网站后Connect一直连不上。所以采取的VLC方式。
打开VLC输入首页提示的地址,即可看到推流成功了。
demo跑通后,便有了一个直观的感受,接下来便是看代码了。
代码
从SpydroidActivity
开始,会看到它连接了一个Service:
1 | private ServiceConnection mRtspServiceConnection = new ServiceConnection() { |
看到RtspServer
中的start方法:
1 | public void start() { |
再看到RequestListener
:
1 | class RequestListener extends Thread implements Runnable { |
这是一个进程类,在构造方法中直接调用了Thread.start(),那么便会执行到run()方法。可以看到,初始化了一个ServerSocket,然后当有客户端连接后(通过VLC输入地址开始播放即是连接到这个ServerSocket),便会执行WorkerThread
线程:
1 | class WorkerThread extends Thread implements Runnable { |
这个类比较长,但是我只需关注采集
。看到processRequest方法
,当有Client连接后,便会有session了,然后打印一些配置之类的信息,最后看到mSession.syncStart(trackId)
,可以猜测这个方法便是开始采集、推流了。
1 | /** |
参数id用来标识是音频,还是视频,最后执行到stream.start()
,其调用的是最顶层的Stream实现类MediaStream
的start方法。
1 | /** Starts the stream. */ |
可以看到,根据Mode选择encodeWithMediaCodec
或是encodeWithMediaRecorder
。
然后看到其继承类的实现方法,这里我只关注了VideoStream
的实现。
1 | /** |
可以看到,整体实现有2个方式:
- MediaRecorder采集数据,通过绑定LocalSocket来获取数据
- 利用Camera又分了2种方式:回调
onPreviewFrame
获取数据进行处理,或者直接输出到Surface
这与之前说到的不谋而合。
问题
通过绑定LocalSocket的方式,我在运行(MI 4LTE Android 6.0.1)的时候出现了MediaRecorder: start failed -38
的错误。经过Google,后面找到解决方案,setOutputFile时使用ParcelFileDescriptor
。作者也在spydroid libstreaming中提到了,并进行了修正。于是我拷贝最新的libstreaming
到工程中,运行的依然出错:MediaRecorder: start failed -2147483648,这下我没招了QAQ。
后面想到github issues
或许也有别人用的时候有这个问题呢,于是我便去看看。但是很不幸,说是在Android 5.0之后不能用MediaRecorder绑定LocalSocket的方式了==>issues#227、issues208、issues#155。
毕竟是好几年前的库了,之前Android都没出到5、6呢,后面作者也没有进行维护了,不过我在OPPA A31(Android 4.4.4)的环境下通过此种方式确实可以运行。
参考
Android 实时视频采集/编码/传输/解码/播放—方案调研(初)
Android 实时视频采集—MediaRecoder录制
libstreaming
spydroid-ipcamera
libstreaming-examples