Android播放器框架分析 1

本文详细分析了Android多媒体播放器框架的核心组件AwesomePlayer的内部工作原理,包括Java层与Native层的交互、AwesomePlayer的关键成员、基本播放流程、视频/音频分离过程以及创建解码器的步骤。通过解析DataSource、MediaExtractor、MediaSource和OMXCodec的使用,揭示了Android视频播放的底层机制。
摘要由CSDN通过智能技术生成

Author:Aya

Date:2011-08-03

 

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

Java代码
  1. MediaPlayer mp = new MediaPlayer();  
  2. mp.setDisplay (...); /// 设置播放器Suface  
  3. mp.setDataSource(PATH_TO_FILE); ///设置媒体URI  
  4. mp.prepare(); /// 初始化播放器  
  5. 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++代码
  1. status_t AwesomePlayer::prepareAsync_l() {  
  2.     if (!mQueueStarted) {  
  3.         mQueue.start();  
  4.         mQueueStarted = true;  
  5.     }  
  6.     return OK;  
  7. }  

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

 

sp<ISurface> mISurface;

供播放器渲染的画布

 

sp<AwesomeRenderer> mVideoRenderer;

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

 

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

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

C++代码
  1. void AwesomePlayer::onPrepareAsyncEvent() {   
  2.         status_t err = finishSetDataSource_l();   
  3. }  

 

C++代码
  1. status_t AwesomePlayer::finishSetDataSource_l() {   
  2.  sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource);   
  3.     return setDataSource_l(extractor);   
  4. }   

 

C++代码
  1. status_t AwesomePlayer::setDataSource_l(const sp<MediaExtractor> &extractor) {   
  2.     for (size_t i = 0; i < extractor->countTracks(); ++i) {   
  3.         sp<MetaData> meta = extractor->getTrackMetaData(i);   
  4.   
  5.         const char *mime;   
  6.         CHECK(meta->findCString(kKeyMIMEType, &mime));   
  7.   
  8.         if (!haveVideo && !strncasecmp(mime, "video/", 6)) {   
  9.             setVideoSource(extractor->getTrack(i));   //
  10.             haveVideo = true;   
  11.         } else if (!haveAudio && !strncasecmp(mime, "audio/", 6)) {   
  12.             setAudioSource(extractor->getTrack(i));   
  13.             haveAudio = true;   
  14.         if (haveAudio && haveVideo) {   
  15.             break;   
  16.         }   
  17.     }   
  18. }  

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

 

sp<MediaSource> mVideoSource;

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

mVideoSource 由OMXCodec创建.

C++代码
  1. status_t AwesomePlayer::initVideoDecoder(uint32_t flags) {   
  2.     mVideoSource = OMXCodec::Create(   
  3.             mClient.interface(), mVideoTrack->getFormat(),   
  4.             false, // createEncoder   
  5.             mVideoTrack,   
  6.             NULL, flags);   
  7. }   

 

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. status_t AwesomePlayer::setDataSource_l(   
  2.         const char *uri, const KeyedVector<String8, String8> *headers) {   
  3.     /// 这里只是把URL保存起来而已, 真正的工作在Prepare之后进行   
  4.     mUri = uri;   
  5.     return OK;   
  6. }  

 

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

C+
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值