Android的MediaPlayerService下一个重要的框架就是stagefright(演讲胆怯者),在它之下就是具体的播放器了。与其关系同等的是NuPlayer,本文将分析stagefright常规框架和一些关键技术。
先看几个类的关系:
class MediaPlayerBase : public RefBase
class MediaPlayerInterface : public MediaPlayerBase
class StagefrightPlayer : public MediaPlayerInterface
struct AwesomePlayer
struct AHandler : public RefBase
struct NuPlayer : public AHandler
struct NuPlayerDriver : public MediaPlayerInterface
以RefBase为基准,MediaPlayerBase和AHandler处于同一级,StagefrightPlayer与NuPlayerDriver为同一级,代表两种不同的架构的播放器。AwesomePlayer与NuPlayer为同一级,代表在两种不同播放器架构下具体的播放器实现。
一般说来,现在本地文件播放用AwesomePlayer,而流媒体用NuPlayer,难道是NuPlayer对RTP处理得更好?
一个播放器通常的架构或者说工作流程为:
创建播放器 – 设置数据源 – 音视频分离 – 编解码 – 渲染 – 画面呈现
先来看下AwesomePlayer的构造函数:
AwesomePlayer::AwesomePlayer()
: mQueueStarted(false),
mAudioPlayer(NULL),
mFlags(0),
mExtractorFlags(0),
mVideoBuffer(NULL),
mDecryptHandle(NULL),
DataSource::RegisterDefaultSniffers();
mVideoEvent = new AwesomeEvent(this, &AwesomePlayer::onVideoEvent);
mStreamDoneEvent = new AwesomeEvent(this, &AwesomePlayer::onStreamDone);
mBufferingEvent = new AwesomeEvent(this, &AwesomePlayer::onBufferingUpdate);
mVideoLagEvent = new AwesomeEvent(this, &AwesomePlayer::onVideoLagUpdate);
mAudioPlayer音频播放器,mFlags播放状态标志,mDecryptHandle解密处理,mVideoBuffer视频处理buffer。
接着注册数据源嗅探器,所谓的嗅探器对应着不同格式的文件,即从一个给定的文件源中,获取相应的字段,以确定文件类型:
DataSource::RegisterDefaultSniffers();
RegisterSniffer_l(SniffMPEG4);
RegisterSniffer_l(SniffMatroska);
RegisterSniffer_l(SniffOgg);
RegisterSniffer_l(SniffWAV);
RegisterSniffer_l(SniffFLAC);
RegisterSniffer_l(SniffAMR);
RegisterSniffer_l(SniffMPEG2TS);
RegisterSniffer_l(SniffMP3);
RegisterSniffer_l(SniffAAC);
RegisterSniffer_l(SniffMPEG2PS);
RegisterSniffer_l(SniffWVM);
char value[PROPERTY_VALUE_MAX];
if (property_get("drm.service.enabled", value, NULL)
&& (!strcmp(value, "1") || !strcasecmp(value, "true"))) {
RegisterSniffer_l(SniffDRM);
}
除了常见的mp3、mp4等还有一个DRM嗅探器(Digital Rights Management数字版权管理)。
最后注册了几个重要事件:
mVideoEvent,mStreamDoneEvent,mBufferingEvent,mVideoLagEvent
当这些事件触发时会调用相关联的函数,即回调函数。
还是从status_t MediaPlayerService::Client::setDataSource(int fd, int64_t offset, int64_t length)
开始,
status_t MediaPlayerService::Client::setDataSource(int fd, int64_t offset, int64_t length) {
player_type playerType = MediaPlayerFactory::getPlayerType(this,fd, offset, length);
sp<MediaPlayerBase> p = setDataSource_pre(playerType);
// now set data source
setDataSource_post(p, p->setDataSource(fd, offset, length));
}
这里playerType=StagefrightPlayer,即AwesomePlayer。创建好AwesomePlayer实例,调用其setDataSource方法:
status_t AwesomePlayer::setDataSource(
int fd, int64_t offset, int64_t length) {
sp<DataSource> dataSource = new FileSource(fd, offset, length);
status_t err = dataSource->initCheck();
return setDataSource_l(dataSource);
}
status_t AwesomePlayer::setDataSource_l(
const sp<DataSource> &dataSource) {
sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource);
if (extractor->getDrmFlag()) {
checkDrmStatus(dataSource);
}
return setDataSource_l(extractor);
}
class FileSource : public DataSource
class DataSource : public RefBase
class MP3Extractor : public MediaExtractor
class MediaExtractor : public RefBase
根据传入的mp3文件构造一个dataSource,然后开始创建分离器:ediaExtractor::Create(dataSource);
p<MediaExtractor> MediaExtractor::Create(
const sp<DataSource> &source, const char *mime) {
if (!source->sniff(&tmp, &confidence, &meta))
mime = tmp.string();
MediaExtractor *ret = NULL;
else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_MPEG)) {
ret = new MP3Extractor(source, meta);
}
RegisterSniffer_l(SniffMP3);
bool SniffMP3(
const sp<DataSource> &source, String8 *mimeType,
float *confidence, sp<AMessage> *meta) {
*meta = new AMessage;
(*meta)->setInt64("offset", pos);
(*meta)->setInt32("header", header);
(*meta)->setInt64("post-id3-offset", post_id3_pos);
*mimeType = MEDIA_MIMETYPE_AUDIO_MPEG;
*confidence = 0.2f;
return true;
}
先嗅探出mime字串,根据dataSource中的mime值MEDIA_MIMETYPE_AUDIO_MPEG,创建一个mp3分离器:MP3Extractor。然后调用setDataSource_l:
status_t AwesomePlayer::setDataSource_l(const sp<MediaExtractor> &extractor)
开始音频和视频数据分离:
status_t AwesomePlayer::setDataSource_l(const sp<MediaExtractor> &extractor) {
CHECK(meta->findCString(kKeyMIMEType, &_mime));
if (!haveVideo && !strncasecmp(mime.string(), "video/", 6)) {
setVideoSource(extractor->getTrack(i));
meta->findInt32(kKeyDisplayWidth, &displayWidth);
meta->findInt32(kKeyDisplayHeight, &displayHeight);
} else if (!haveAudio && !strncasecmp(mime.string(), "audio/", 6)) {
setAudioSource(extractor->getTrack(i));
}
mExtractorFlags = extractor->flags();
分离前先从mp3文件中找到kKeyMIMEType结构,标识着video还是audio。视频分离setVideoSource(extractor->getTrack(i));同时将视频显示的高度和宽度记录下来。音频分离也很简单:setAudioSource(extractor->getTrack(i));
分离完成后,设置分离之后的标志:
CAN_SEEK_BACKWARD | CAN_SEEK_FORWARD | CAN_PAUSE | CAN_SEEK;
这样AwesomePlayer的实例就创建好了,并且数据设置完毕,音视频也分离开来,成为独立的数据源。