悠悠岁月 岁月匆匆

Author:Aya

Date:2011-08-03

Java层 要开启一个播放器进行播放, 需要以下几行代码:

Java代码

mp.start();///开始播放

MediaPlayer 的Native 层 实际上是由 stagefright 模块 以及 OMX 模块组成, 其中stagefright 依赖OMX进行编解码. (据说 stagefright 和OMX 本质上是利用OpenBinder进行通讯,不过我没研究这里)

1. AwesomePlayer

忽略掉 JNI 封装层, Stagefright 从 AP开始. AP 是Stagefright核心类. AP有一些接口甚至与MediaPlayer 是一一对应的例如setDataSource,prepare.

1.1 关键成员分析

OMXClient mClient

AP利用OMXClient 跟OMX IL进行通信. 这里OMX IL类似于一个服务端. AP 作为一个客户端, 请求OMX IL进行解码的工作.

TimedEventQueue mQueue

AP采用定时器队列的方式进行运作. mQueue 在MediaPlayer调用 prepare的时候就开始运作, (而不是MediaPlayer.start).

C++代码

AP处理了几个定时器事件, 包括 onVideoEvent(); onStreamDone(); onBufferingUpdate();onCheckAudioStatus() ; onPrepareAsyncEvent();

sp<ISurface> mISurface;

供播放器渲染的画布

sp<AwesomeRenderer> mVideoRenderer;

负责将解码后的图片渲染输出

sp<MediaSource> mVideoTrack;sp<MediaSource> mAudioTrack;

分别代表一个视频轨道和音频轨道, 用于提取视频帧和音频帧. mVideoTrack和mAudioTrack 在 onPrepareAsyncEvent事件被触发时, 由MediaExtractor分离出来.

C++代码

C++代码

C++代码

从上面这段代码可以看到AwesomePlayer默认采用第一个VideoTrack和第一个AudioTrack, 那如何切换VideoTrack和AudioTrack?

sp<MediaSource> mVideoSource;

mVideoSource 可以认为是一个视频解码器的封装, 用于产生视频图像供AwesomeRender渲染, mVideoSource的数据源则由mVideoTrack提供.

mVideoSource 由OMXCodec创建.

C++代码

sp<MediaSource> mAudioSource; sp<MediaPlayerBase::AudioSink> mAudioSink; AudioPlayer *mAudioPlayer;

mAudioSource 也可以认为是一个音频解码器的封装

mAudioSink 代表一个音频输出设备. 用于播放解码后的音频数据. (AudioSink is used for in-memory decode and potentially other applications where output doesn’t go straight to hardware)

mAudioPlayer 把mAudioSource和mAudioSink 包起来,完成一个音频播放器的功能. 如start, stop, pause, seek 等.

AudioPlayer和 AudioSink通过Callback建立关联. 当AudioSink可以输出音频时,会通过回调通知AudioPlayer填充音频数据. 而此时AudioPlayer 会尝试从AudioSource 读取音频数据.

总结: 从关键的成员可以看出, AwesomePlayer 拥有视频源和音频源 (VideoTrack, AudioTrack), 有音视频解码器(VideoSoure, AudioSource), 可以渲染图像 (AwesomeRenderer), 可以输出声音 (AudioSink), 具备一个播放器完整的材料了.

1.2 基本播放流程

1.2.1 设置数据源URI

C++代码

1.2.2 开启定时器队列,并且 Post一个AsyncPrepareEvent 事件

C++代码

Prepare 之后, AwesomePlayer 开始运作.

1.2.3 AsyncPrepare 事件被触发

当这个事件被触发时, AwesomePlayer 开始创建 VideoTrack和AudioTrack , 然后创建 VideoDecoder和AudioDecoder

C++代码

至此,播放器准备工作完成, 可以开始播放了

1.2.4 Post 第一个VideoEvent

AwesomePlayer::play()调用 -> AwesomePlayer::play_l() 调用 -> AwesomePlayer::postVideoEvent_l(int64_t delayUs)

C++代码

1.2.5 VideoEvent 被触发

C++代码

总结: SetDataSource -> Prepare -> Play -> postVieoEvent -> OnVideoEvent -> postVideoEvent-> …. onVideoEvent-> postStreamDoneEvent -> 播放结束

1.3 视频 / 音频 分离

1.3.1 创建DataSource

如前面提到的, 当AsyncPrepare 事件被触发时, AwesomePlayer会调用 finishSetDataSource_l 创建 VideoTrack 和 AudioTrack.

finishSetDataSource_l 通过URI前缀判断媒体类型, 比如 http, rtsp,或者本地文件等 这里的uri就是一开始 通过setDataSource设置的 根据uri 创建相应的DataSource, 再进一步的利用 DataSource 创建MediaExtractor做A/V分离

C++代码

1.3.2 根据DataSource 创建MediaExtractor

先看看 AwesomePlayer 的构造函数,里面有一行代码.

C++代码

RegisterDefaultSniffers 注册了一些了媒体的MIME类型的探测函数.

C++代码

这些探测用于判断媒体的MIME类型, 进而决定要创建什么样的MediaExtractor.

再回到 MediaExtractor::Create, MediaExtractor对象在这里创建. 下面代码有点长, 其实这段代码只是根据MIME类型创建Extractor的各个分支而已.

TCC在这个函数添加了一点代码, 用于创建自己的Extractor 对象.

C++代码

(可以看到几乎所有封装格式都支持. 那为什么播放 Asf +H264的文件会失败呢? 只能怀疑ITCCMediaExtractor实现的有问题了)

1.3.3 根据MediaExtractor 做A/V分离

再看看 setDataSource_l(const sp<MediaExtractor> &extractor) , 这是A/V分离的最后步骤.

C++代码

从这个函数可以看到MediaExtractor 需要实现的基本比较重要的接口 (这个几个接口都是纯虚函数, 可见Extractor的子类是一定要搞定它们的)

virtual size_t countTracks() = 0; /// 该媒体包含了几个Track?

virtual sp<MediaSource> getTrack(size_t index) = 0; /// 获取指定的Video/Audio Track, 可以看到一个Track本质上就是一个MediaSource.

有多远,走多远,把足迹连成生命线。

悠悠岁月 岁月匆匆

相关文章:

你感兴趣的文章:

标签云: