Android 2.3 Midia Framework

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播放过程。

发布了26 篇原创文章 · 获赞 24 · 访问量 9万+
展开阅读全文

使用URL如何播放视频?

01-04

我使用媒体播放器,试图从youtube上下载视频和音频流。 使用 url如何实现呢? 我使用的例子是: http://www.youtube.com/watch?v=SgGhtjKWLOE&feature=feedrec 但是得到的 LogCat Error: 01-03 22:22:30.140: INFO/AwesomePlayer(85): setDataSource_l('http://www.youtube.com/watch?v=I3jv0IF9n6A') 01-03 22:22:30.140: INFO/NuHTTPDataSource(85): connect to www.youtube.com:80/watch?v=I3jv0IF9n6A @0 01-03 22:22:30.250: INFO/NuHTTPDataSource(85): connect to m.youtube.com:80/watch?desktop_uri=http%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DI3jv0IF9n6A&v=I3jv0IF9n6A&gl=US @0 09-04 22:22:30.410: INFO/NuHTTPDataSource(85): connect to m.youtube.com:80/#/watch?desktop_uri=http%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DI3jv0IF9n6A&v=I3jv0IF9n6A&gl=US @0 01-03 22:22:30.570: INFO/NuHTTPDataSource(85): Chunked transfer encoding applied. 01-03 22:22:30.570: WARN/NuHTTPDataSource(85): Server did not give us the content length! 01-03 22:22:31.000: INFO/NuCachedSource2(85): ERROR_END_OF_STREAM 01-03 22:22:31.630: ERROR/MediaPlayer(8404): error (1, -2147483648) 01-03 22:22:31.630: WARN/System.err(8404): java.io.IOException: Prepare failed.: status=0x1 01-03 22:22:31.630: WARN/System.err(8404): at android.media.MediaPlayer.prepare(Native Method) 01-03 22:22:31.630: WARN/System.err(8404): at com.fttech.example.youtube.example.AccessibleYouTube.watchVideo(AccessibleYouTube.java:139) 01-03 22:22:31.630: WARN/System.err(8404): at java.lang.reflect.Method.invokeNative(Native Method) 01-03 22:22:31.630: WARN/System.err(8404): at java.lang.reflect.Method.invoke(Method.java:491) 01-03 22:22:31.630: WARN/System.err(8404): at android.view.View$1.onClick(View.java:2678) 01-03 22:22:31.630: WARN/System.err(8404): at android.view.View.performClick(View.java:3110) 01-03 22:22:31.630: WARN/System.err(8404): at android.view.View$PerformClick.run(View.java:11928) 01-03 22:22:31.630: WARN/System.err(8404): at android.os.Handler.handleCallback(Handler.java:587) 01-03 22:22:31.630: WARN/System.err(8404): at android.os.Handler.dispatchMessage(Handler.java:92) 问答

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览