Android播放器框架分析 1
Author:Aya
Date:2011-08-03
the1soft
Java层 要开启一个播放器进行播放, 需要以下几行代码:
Java代码
MediaPlayer mp = new MediaPlayer();
mp.setDisplay (...); /// 设置播放器Suface
mp.setDataSource(PATH_TO_FILE); ///设置媒体URI
mp.prepare(); /// 初始化播放器
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++代码
status_t AwesomePlayer::prepareAsync_l() {
if (!mQueueStarted) {
mQueue.start();
mQueueStarted = true;
}
return OK;
}
AP处理了几个定时器事件, 包括 onVideoEvent(); onStreamDone(); onBufferingUpdate();onCheckAudioStatus() ; onPrepareAsyncEvent();
sp<ISu易做图ce> mISu易做图ce;
供播放器渲染的画布
sp<AwesomeRenderer> mVideoRenderer;
负责将解码后的图片渲染输出
sp<MediaSource> mVideoTrack; sp<MediaSource> mAudioTrack;
分别代表一个视频轨道和音频轨道, 用于提取视频帧和音频帧. mVideoTrack和mAudioTrack 在 onPrepareAsyncEvent事件被触发时, 由MediaExtractor分离出来.
C++代码
void AwesomePlayer::onPrepareAsyncEvent() {
status_t err = finishSetDataSource_l();
}
C++代码
status_t AwesomePlayer::finishSetDataSource_l() {
sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource);
return setDataSource_l(extractor);
}
C++代码
status_t AwesomePlayer::setDataSource_l(const sp<MediaExtractor> &extractor) {
for (size_t i = 0; i < extractor->countTracks(); ++i) {
sp<MetaData> meta = extractor->getTrackMetaData(i);
const char *mime;
CHECK(meta->findCString(kKeyMIMEType, &mime));
if (!haveVideo && !strncasecmp(mime, "video/", 6)) {
setVideoSource(extractor->getTrack(i)); //
haveVideo = true;
} else if (!haveAudio && !strncasecmp(mime, "audio/", 6)) {
setAudioSource(extractor->getTrack(i));
haveAudio = true;
if (haveAudio && haveVideo) {
break;
}
}
}
从上面这段代码可以看到AwesomePlayer默认采用第一个VideoTrack和第一个AudioTrack, 那如何切换VideoTrack和AudioTrack?
sp<MediaSource> mVideoSource;
mVideoSource 可以认为是一个视频解码器的封装, 用于产生视频图像供AwesomeRender渲染, mVideoSource的数据源则由mVideoTrack提供.
mVideoSource 由OMXCodec创建.
C++代码
status_t AwesomePlayer::initVideoDecoder(uint32_t flags) {
mVideoSource = OMXCodec::Create(
mClient.inte易做图ce(), mVideoTrack->getFormat(),
false, // createEncoder
mVideoTrack,
NULL, flags);
}
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++代码
status_t AwesomePlayer::setDataSource_l(
const char *uri, const KeyedVector<String8, String8> *headers) {
/// 这里只是把URL保存起来而已, 真正的工作在Prepare之后进行
mUri = uri;
return OK;
}
1.2.2 开启定时器队列,并且 Post一个AsyncPrepareEvent 事件
C++代码
status_t AwesomePlayer::prepareAsync_l() {
/// 开启定时器队列
mQueue.start();
/// Post AsyncPrepare 事件
mAsyncPrepareEvent = new AwesomeEvent(
this, &AwesomePlayer::onPrepareAsyncEvent);
mQueue.postEvent(mAsyncPrepareEvent);
return OK;
}
Prepare 之后, AwesomePlayer 开始运作.
1.2.3 AsyncPrepare 事件被触发
当这个事件被触发时, AwesomePlayer 开始创建 VideoTrack和AudioTrack , 然后创建 VideoDecoder和AudioDecoder
C++代码
void AwesomePlayer::onPrepareAsyncEvent() {
/// a. 创建视频源和音频源
finishSetDataSource_l();
/// b. 创建视频解码器
initVideoDecoder();
/// c. 创建音频解码器
initAudioDecoder();
}
至此,播放器准备工作完成, 可以开始播放了
1.2.4 Post 第一个VideoEvent
AwesomePlayer::play() 调用
补充:移动开发 , Android ,