1. MediaPlayer介绍

MediaPlayer是Android api提供的用来播放音频和视频的类, VideoView也是对其简单的封装。 用MediaPlayer播放视频很简单, 需要4个步骤。

l通过mediaPlayer.setDataSource设置要播放的uri。

l通过mediaPlayer.setDisplay函数设置要显示到哪个surface。

l通过mediaPlayer.prepare做播放前的准备。

l通过mediaPlayer.play开始播放。

2. Media Framework整体架构

MediaPlayer位于应用程序的进程, 很可能会有多个位于不同进程的多个MediaPlayer实例存在。MediaPlayerService位于mediaserver进程,系统中只有单一的实例。mediaserver进程在系统启动时就开始运行media相关的服务,代码如下:

main_mediaserver.cpp:

int main(int argc, char** argv)

{

sp<ProcessState> proc(ProcessState::self());

sp<IServiceManager> sm = defaultServiceManager();

LOGI("ServiceManager: %p", sm.get());

AudioFlinger::instantiate();

MediaPlayerService::instantiate();

CameraService::instantiate();

AudioPolicyService::instantiate();

ProcessState::self()->startThreadPool();

IPCThreadState::self()->joinThreadPool();

}

对于每一个MediaPlayer实例的请求, MediaPlayerService都会new一个StageFrightPlayer实例去接待。StageFrightPlayer仅充当adapter的角色,将工作代理给AwesomePlayer来做。

3. 深入分析AwesomePlayer

MediaPlayer的四部曲: setDataSource,setDisplay prepare和play。

同样, AwesomePlayer中也有四部曲与之对应: setDataSource, setISurface,prepare, play。

AwesomePlayer将DataSource的缓冲单独放在一个进程中进行, 将音视频分离,视频解码, 视频渲染都放在一个线程中进行。音频解码和播放在其它线程中进行, 视频依据音频播放的时间戳来同步播放视频。

setDataSource过程:没做什么工作, 只是把参数uri存进mUri中。

setISurface过程:mISurface = isurface

图:AwesomePlayer prepare过程的数据流程图

prepare过程:如上图所示。

l根据mUri来newDataSource的子类,当uri以http://开头时, 就会创建NuHttpDataSource, 就可以从mDataSource中读到媒体文件的原始数据。

l调用工厂方法MediaExactor.create根据从mDataSource读到的数据判断文件格式来new MediaExactor的子类, MP4格式就对应MEPG4Exactor, 用它来分离mDataSource最终得到视频轨道mVideoTrack和音频轨道mAudioTrack, 就可以单独从mVideoTrack和mAudioTrack中读到未经解码的音频和视频数据。

l从mVideoTrack和mAudioTrack可以获得编码格式, 通过OMXCodec::Create来生成mVideoSource和mAudioSource, 就能从mVideoSource和mAudioSource就可以读到解码后的音视频数据了,现在一切prepare好了。

Play过程: AwesomePlayer将mAudioSource交给AudioPlayer播放,而自己创建线程去主动与AudioPlayer取得同步, 通过mVideoSource读取解码后的数据, 然后交给mVideoRenderer(AwesomeRenderer)把数据render到据有的surface上。

图:AwesomePlayer的组成架构图,图片太大,如显示不全可右键下载下来看

上图充满了一种设计模式叫Decorator(装饰),NuCachedSource2装饰了DataSource, 给它添加了缓冲功能。 MP3Decoder和OMXCodec装饰了MediaSource, 给它添加了解码功能。Decorator模式一般都是通过继承+聚合(或组合)实现的, 能看出设计模式,才能对代码为什么张成这样有理解。

上图中红色框框中的部分是系统的变动点,可以把框框里的部分比作树叶, 框框外的部分比作树干,树叶每年更换, 树干主体相对不变, 而最接近树叶的连接处需要应树叶之变。

如果要增加新的协议,只需在协议引擎长出一只树叶,即写个DataSource子类, 实现抓取数据的接口, 并在树干上张出和树叶相连的节点, 即在AwesomePlayer.onPrepareAsyncEnent根据uri来创建datasource。

如果要新增对其它媒体格式的支持, 比如flv, 需要在音视频轨道分离引擎新增flv对应的MediaExactor子类,如果用软解码器, 需要在软解码引擎部分写个子类继承并聚合MediaSource来给它穿上解码的“Decorator”。如果用硬解码器,就有两种方法,符合现有框架的方法是在硬解码引擎部分添加OMXPlugin。还有一种方法, 那就是将OpenMax一不做二不休的踢出去, 将蓝色矩形框表示的硬解码框架整个替换。

如果要利用硬件加速模块加速视频渲染过程(利用Overlay),就需要在渲染引擎的libstagefrighthw.so中实现VideoRenderer接口, 实例可参考“mydroid”/device目录下三星或htc的libstagefrighthw实现。

4. NuHttpDataSource的实现

NuHttpDataSource实现了DataSource的接口, 并新增了一个public函数connect, connect函数在prepare过程中被调用。Connect过程中首先通过HttpSream.connect建立socket连接, 然后给uri发送http get请求, 等待服务器的回应, 分析回应中的http head, 处理redirect的情况, 还从http head中得到文件长度等信息。

最重要的就是实现的DataSource的readAt接口,NuHttpDataSource的readAt通过HttpStream接口读网络数据, HttpStream则通过读socket来实现。

5. NuCachedSource2的智慧

NuCachedSource2可以说是Decorator模式的经典范例, 继承了DataSource的接口, 同时又聚合了一个将被Cached的DataSource, 这个DataSource在构造函数中传入, 如下所示。

struct NuCachedSource2 : public DataSource {

NuCachedSource2(constsp<DataSource> &source);

……

}

在创建之初, NuCachedSource2就开始对DataSource进行缓冲, 它首先创建一个Alooper, Alooper内涵一个线程, 然后把一个AHandlerReflector挂在这个looper上跑, 最后发送kWhatFetchMore命令来启动缓冲。

NuCachedSource2::NuCachedSource2(constsp<DataSource> &source)

:mReflector(newAHandlerReflector<NuCachedSource2>(this))

: mSource(source),

mLooper(new ALooper),

……

mLooper->setName("NuCachedSource2");

mLooper->registerHandler(mReflector);

mLooper->start();

Mutex::AutolockautoLock(mLock);

(newAMessage(kWhatFetchMore, mReflector->id()))->post();

}

当mLooper的线程收到kWhatFetchMore命令时, 就会调用mSource.readAt函数读取1 Page大小的数据, 把读到存放到PageCache::Page结构里, 然后把Page添加进PageCache。

PageCache的大小和Page的大小在NuCachedSource2.h里定义:

enum {

kPageSize =65536,//Page size

kHighWaterThreshold = 5 * 1024 * 1024,// PageCache上限

kLowWaterThreshold = 512 * 1024,

};

当缓冲了1 Page数据后, 在mLooper的线程里会给自己发送一条kWhatFetchMore消息,之后继续上面的过程, 直到PageCache满或停止播放。

当调用NuCachedSource2的readAt函数读数据时, 就会从PageCache拿出Page里的数据给reader, 然后释放掉Page。

6.支持html5网页视频的方法

首先, 浏览器访问视频网站时要上报ipad的useragent, 如果视频网站支持html5, 就会回传含video标签的html5网页。

处理含video标签的html5网页的流程如下:

含video标签的html5网页->本地webkit解析出url->Html5VideoProxy.java->WebView

->StageFright。

最终AweSomePlayer会得到一个m3u文件的url,何为m3u文件?请看百度百科。

下图为StageFright处理m3u列表的架构图。


AweSomePlayer收到url后调用LiveSource.isLiveSource判断是否是m3u文件,如果是就走LiveSource的流程。M3u列表中, 多个url可能对应同一视频的不同时间段,DISCONNECTIVITY标签意味着下个url是新视频文件的开始。 视频播放过程中, 当遇到DISCONNECTIVITY标签时,LiveSource会插入一段128(一个ts package大小)字节的空包, Mpeg2TsExtractor读到空包时,会通知Parser。

AwesomePlayer中,Decoder读Parser时如果Parser返回INFO_DISCONNECTIVITY, 就会重置解码器, 准备解码下一段新视频。

7.StageFright中rtsp协议的支持

打开StageFright中的rtsp支持, 需要设置media.stagefright.enable-rtsp=1(or true)属性。

8. 参考文献

http://iamkcspa.pixnet.net/blog。

TODO:

Rtsp支持分析。

如何实现OMXPlugin。

Audio播放过程。

更多相关文章

  1. 一句话锁定MySQL数据占用元凶
  2. Android(安卓)数据传递-通过剪切板传递数据
  3. Android(安卓)串口通信之间的发送数据与接收数据(详解)
  4. 使用 SQLiteDatabase 操作 SQLite 数据库
  5. android 两个视频同时播放音频冲突的解决
  6. Android之ListView的简单用法
  7. Android开发之数据存储全方案
  8. Android数据储存——文件储存
  9. android 播放 优酷视频

随机推荐

  1. 伪类选择器与盒模型常用属性
  2. CSS之伪类选择器和简单盒子简单案例
  3. Android中不规则形状View的布局实现
  4. [随便写写]Android基础教程
  5. Android开发之旅:应用程序基础及组件
  6. Android(安卓)adb环境配置
  7. android handler总结
  8. Android统计图表MPAndroidChart:为多条统
  9. andorid handler 消息传递机制
  10. 如何使用Mindjet Maps for Android中的移