Android 9.0 multimedia框架解析(四)start过程

  • Post author:
  • Post category:其他




概述

MediaPlayer的start过程会调用到MediaPlayerService::Client的start函数,再调用Nuplayer对Decoder和MediaCodec进行初始化,然后找到解码器开始进行解码的工作。



类说明

1.Nuplayer::Decoder:解码模块。在播放的时候会调用MediaCodec去获取编解码列表,选择合适解码库。

2.MediaCodec:Decoder的代工。根据Decoder的要求,去MediaCodecList中查找解码器信息,然后选择合适的解码库。负责跟ACodec进行数据交互。

3…ACodec:主要负责解码相关工作。通过IOMXNode去控制解码器:设置参数,获取参数等。将从ACodecBufferChannel获取的解析之后的文件数据送给解析器。



总bouml时序图

下图的橙色边框内的时序就是start过程相关的。

在这里插入图片描述

start过程可以分为下面几个流程:

1.启动解析器,启动GenericSource的start过程,从MP3Source中读取解析后的数据。

2.创建Nuplayer::Decoder,初始化Decoder。

3.根据mine类型查找并创建解码器。



start解析器

MediaPlayerService::Client的start函数经过层层调用会调用到NuPlayer::onStart,这个过程从时序图就可以看出来了,比较简单。NuPlayer::onStart函数一进来就对mSourceStarted进行判断,如果还没有启动则调用GenericSource::start。下面是相关代码。

void NuPlayer::onStart(int64_t startPositionUs, MediaPlayerSeekMode mode) {
    if (!mSourceStarted) {
        mSourceStarted = true;
        mSource->start();
    }
   	......
}
void NuPlayer::GenericSource::start() {
    if (mAudioTrack.mSource != NULL) {
        postReadBuffer(MEDIA_TRACK_TYPE_AUDIO);
    }
    ......
}

咦,postReadBuffer不就是prepare流程会调用到的函数吗,它会发送kWhatReadBuffer给自己的消息处理函数,然后启动onReadBuffer的操作,从解析器的source,比如MP3Source中读取64帧的buffer。这个过程上一节已经分析过了,这里就不分析了。



初始化Decoder

Decoder是Nuplayer的一个内部类,它在start流程中的instantiateDecoder被初始化。start流程是如何调用到instantiateDecoder,可以直接看时序图。

status_t NuPlayer::instantiateDecoder(bool audio, sp<DecoderBase> *decoder, bool checkAudioModeChange){
	 sp<AMessage> format = mSource->getFormat(audio);	//从解析器获取格式信息
	 if (audio) {
		if (mOffloadAudio){
		}else{	//我这里不是mOffloadAudio,所以走这里
	 		*decoder = new Decoder(notify, mSource, mPID, mUID, mRenderer);	//创建decoder
	 	}
	 }
	 (*decoder)->init();	
	 (*decoder)->configure(format);	//把格式信息传递给decoder
}


获取格式信息

先来看看mSource->getFormat是如何获取到格式信息的。mSource是Nuplayer::Source类型的,前面已经知道了mSource保存的是Nuplayer::Source的子类Nuplayer::GenericSource对象。由于GenericSource没有实现getFormat,所以这里先调用了Source::getFormat。

接着调用了子类GenericSource的getFormatMeta,getFormatMeta再去调用到getFormatMeta_l。

sp<AMessage> NuPlayer::Source::getFormat(bool audio) {
    sp<MetaData> meta = getFormatMeta(audio);
    sp<AMessage> msg = new AMessage;
    convertMetaDataToMessage(meta, &msg) == OK; 
}
sp<MetaData> NuPlayer::GenericSource::getFormatMeta_l(bool audio) {
    sp<IMediaSource> source = audio ? mAudioTrack.mSource : mVideoTrack.mSource;
    return source->getFormat();
}

我们上一节已经知道了mAudioTrack.mSource就是MP3Source wrapper的binder调用。所以这里source->getFormat()最终调用的是 MP3Source::getFormat。这个函数会返回MetaDataBase mMeta,这个是一个存储类,用于保存文件的详细信息,比如采样率,比特流,通道数量等等。

status_t MP3Source::getFormat(MetaDataBase &meta) {
    meta = mMeta;
    return OK;
}
MP3Extractor::MP3Extractor(...){
	 ......
	 mMeta.setInt32(kKeySampleRate, sample_rate);
	 mMeta.setInt32(kKeyBitRate, bitrate * 1000);
	 mMeta.setInt32(kKeyChannelCount, num_channels);
	 ......
}

这里我们知道了原来获取格式就是去获取MetaData,但是在返回调用的时候又调用了convertMetaDataToMessage。顾名思义,这个函数的作用就是把MetaData转换成AMessage,AMessage也是信息存储的一个类。



配置Decoder

获得格式信息之后,会调用一个比较重要的函数NuPlayer::Decoder::onConfigure,我们来看看这个函数做了什么。

struct NuPlayer::Decoder : public DecoderBase {
 	sp<MediaCodec> mCodec;
 }
void NuPlayer::Decoder::onConfigure(const sp<AMessage> &format) {
	mCodec = MediaCodec::CreateByType(mCodecLooper, mime.c_str(), false /* encoder */, NULL /* err */, mPid, mUid);
	.......
}

可以看出函数onConfigure会先去创建MediaCodec,来看看CreateByType会去做什么操作

sp<MediaCodec> MediaCodec::CreateByType(...const AString &mime...){
	Vector<AString> matchingCodecs;
	 MediaCodecList::findMatchingCodecs(mime.c_str(),encoder, 0,&matchingCodecs);
    for (size_t i = 0; i < matchingCodecs.size(); ++i) {	//我这里是音频,所以只有一个
        sp<MediaCodec> codec = new MediaCodec(looper, pid, uid);	//创建MediaCodec
        AString componentName = matchingCodecs[i];
        status_t ret = codec->init(componentName);		//把解码器名字传递给MediaCodec
    }
}

MediaCodecList::findMatchingCodecs的过程有点绕,直接来看时序图。

在这里插入图片描述

从时序图可以看出这个过程会去通过MediaPlayerService获取server端的MediaCodecList。如果没有已存在的解码器列表,会去重新获取,从omxStore中去获取列表。omxStore是一个驻留在Main_codecservice中的服务,它会在开机的时候使用MediaCodecsXmlParser去解析xml文件 ,获取到编解码器列表。omx相关的东西后续会开一节来讲解。

来看看codec->init(componentName)操作。componentName是一个形如OMX.google.vorbis.decoder的AString。

struct MediaCodec : public AHandler {
	sp<CodecBase> mCodec;	//struct ACodec : public AHierarchicalStateMachine, public CodecBase 
} 
status_t MediaCodec::init(const AString &name) {
	mCodec = GetCodecBase(name);	
}
sp<CodecBase> MediaCodec::GetCodecBase(const AString &name) {
    if (name.startsWithIgnoreCase("c2.")) {
        return CreateCCodec();
    } else if (name.startsWithIgnoreCase("omx.")) {	//我这边都是omx的,所以都是new ACodec
        return new ACodec;
    } else if......
}

到了这里ACodec已经创建完毕了,并且已经保存在MediaCodec的成员mCodec中了,往后就是通过MediaCodec来控制ACodec,继而控制解码器了。来梳理一下decoder初始化流程:

1.NuPlayer创建Decoder,保存在NuPlayer的sp<DecoderBase> mAudioDecoder成员中。

2.在创建MediaCodec之前会去查找编解码列表,找到匹配的omx组件。

3.创建MediaCodec,保存在Decoder的sp<MediaCodec> mCodec成员中。配置MediaCodec时把匹配到的omx组件传递给它。

4.在MediaCodec::init中创建ACodec,保存在MediaCodec的sp<CodecBase> mCodec成员中。

接下来就是ACodec从ACodecBufferChannel获取解析后的数据流,然后传给真正的解码器去解码了。这个过程后面整理好相关逻辑再来讲…



版权声明:本文为qq_27136111原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。