音频的回放流程-播放器的创建及数据准备(提取,解码)

这篇文章的目的是疏通从应用端到服务端到本地库之间的播放链路是怎么打通的,其中涉及的具体细节,比如AMessage,ALooper机制,codec,rendererAduioTrackAudioFlingerbuffer的共享等,没有详细分析。


根据mediaplayer的状态机,一个音频的回放通常是从idle经过setDataSource()Initialized状态,然后通过prepare()prepared状态,最后调用start()进入started状态。


MediaPlayerplayer = new MediaPlayer();

player.setDataSource(path);//文件路径可以是uri,或者本地路径

player.setAudioStreamType(AudioManager.STREAM_MUSIC); //设置流类型

player.prepare(); //调用prepare方法

player.start();//调用start开始播放

有关的类:

//提供给应用层的api接口类

frameworks/base/media/java/android/media/MediaPlayer.java

对应的jni中介类:

frameworks/base/media/jni/android_media_mediaplayer.cpp

对应的本地层实现:

frameworks/av/media/libmedia/Mediaplayer.cpp

音频回放的核心类:

Frameworks/av/media/libaudioclient/AudioTrack.cpp

播放器类:

Frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp

  1. 创建mediaplayer(java)实例,在播放结束时,要调用其release()释放跟mediaplayer对象有关的资源

frameworks/base/media/java/android/media/mediaPlayer.java
public MediaPlayer() {
//调用jni层实现,会创建本地的mediaplayer(cpp)实例。
	native_setup(new WeakReference<MediaPlayer>(this));
}
Frameworks/base/media/jni/android_media_mediaplayer.cpp
static void android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this){
//创建本地层的mediaplayer,mediaplayer.cpp的构造函数其实没做什么事情,只是一些变量的初始化。
	sp<MediaPlayer> mp = new MediaPlayer();

 //创建一个到mediaplayer的监听,用来通知事件到应用线程,也就是notify的形式。
    sp<JNIMediaPlayerListener> listener = new JNIMediaPlayerListener(env, thiz, weak_this);
    mp->setListener(listener);

//把本地层的mediaplayer实例,保存在fields.context变量中,方便后面读取。
	setMediaPlayer(env, thiz, mp);
}

2,setDataSource()实现。

以本地文件为例:

frameworks/base/media/java/android/media/mediaPlayer.java
public void setDataSource(FileDescriptor fd, long offset, long length){
	_setDataSource(fd, offset, length);
}

直接调用了jni实现:

Frameworks/base/media/jni/android_media_mediaplayer.cpp
static void
android_media_MediaPlayer_setDataSourceFD(JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length){
	sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
	int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
//主要是调用本地MediaPlayer实例mp的setdatasource,把文件句柄往下传,如果mp->setDataSource返回了错误的状态,将在process_media_player_call中notify应用层。
	process_media_player_call( env, thiz, mp->setDataSource(fd, offset, length), "java/io/IOException", "setDataSourceFD failed." );
}

继续看本地层的setdatasource实现:

Frameworks/av/media/libmedia/MediaPlayer.cpp
status_t MediaPlayer::setDataSource(int fd, int64_t offset, int64_t length){
//获取mediaplayerservice句柄。
	const sp<IMediaPlayerService> service(getMediaPlayerService());
//调用mps的create方法,创建一个IMediaPlayer实例,作为应用端的mediaplayer跟mediaserver进程间的跨进程通信桥梁。IMediaPlayer对应服务端的实现是mediaplayerservice.cpp的内部类Client。
	sp<IMediaPlayer> player(service->create(this, mAudioSessionId));
//调用mps的client的setdatasource。
player->setDataSource(fd, offset, length);
//把跟mediaplayerservice通信的桥梁跟mediaplayer.cpp绑定,并把这个实例保存在mediaplayer.cpp中,具体是:sp<IMediaPlayer> mPlayer
err = attachNewPlayer(player);
}

接着看mediaplayerservice端对setdatasource的处理:

Frameworks/av/media/libmediaplayerservice/mediaplayerservice.cpp
status_t MediaPlayerService::Client::setDataSource(int fd, int64_t offset, int64_t length){
//step1,获取播放类型,目前默认的播放类型有STAGEFRIGHT_PLAYER = 3, NU_PLAYER = 4, TEST_PLAYER = 5,三种。
player_type playerType = MediaPlayerFactory::getPlayerType(this, fd,  offset, length);	
//step2,根据前一步的播放类型,创建相应的播放器。
sp<MediaPlayerBase> p = setDataSource_pre(playerType);
//step3,调用具体播放器的setDataSource,
return mStatus = setDataSource_post(p, p->setDataSource(fd, offset, length));
}

根据log,播放的是本地MP3,类型是NU_PLAYER

01-0400:41:52.843 V/MediaPlayerService( 544): player type = 4

01-0400:41:52.843 V/MediaPlayerFactory( 544): create NuPlayer


Step1,具体播放类型,是由mediaplayerfactory中注册的类型匹配的。msm8909AndroidO版本,本地播放、流媒体播放,选择的都是nuplayerdriver,只注册NuPlayerFactory,所以回放本地、流媒体都选择的是NuPlayerDriver。而录音时选择的是StagefrightRecorder。实际上真正的播放类是NuPlayerNuPlayerDriver是对NuPlayer的一个包装,使用了ALooperAMessage的消息机制。

Step2setDataSource_pre()

sp<MediaPlayerBase> MediaPlayerService::Client::setDataSource_pre(
        player_type playerType){
//根据播放类型,创建具体的播放器器,这里是创建的NuPlayerDriver播放器,实际的播放类是NuPlayer.cpp。
	sp<MediaPlayerBase> p = createPlayer(playerType);
//这一步的作用,设置音频流的接收端sink端,音频元数据可以认为是输入端,这里的AudioOutput认为是输出端。
    if (!p->hardwareOutput()) {
        mAudioOutput = new AudioOutput(mAudioSessionId, IPCThreadState::self()->getCallingUid(),
                mPid, mAudioAttributes);
        static_cast<MediaPlayerInterface*>(p.get())->setAudioSink(mAudioOutput);
    }
}

Step3,调用实际播放器的setDataSource

Frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
status_t NuPlayerDriver::setDataSource(int fd, int64_t offset, int64_t length) {
	mPlayer->setDataSourceAsync(fd, offset, length);
}
Frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
void NuPlayer::setDataSourceAsync(int fd, int64_t offset, int64_t length) {
//对这个消息的处理,一是保存一个GenericSource实例,而是发出SetDataSourceCompleted的通知。
	sp<AMessage> msg = new AMessage(kWhatSetDataSource, this);
	sp<AMessage> notify = new AMessage(kWhatSourceNotify, this);

	sp<GenericSource> source = new GenericSource(notify, mUIDValid, mUID);
	status_t err = source->setDataSource(fd, offset, length);
    msg->setObject("source", source);
    msg->post();
}

到这里setdatasource就执行完了,这个过程主要是实际播放器的创建,及AudioSink的设置。媒体播放从上到下是通过事件机制下发,从下到上是通过notify方式回调。

其中AudioOutput/Audiosink的创建后,传给实际的播放器,以mAudioSink保存,它将用于音频数据的输出,最终通过AudioTrackAudioFlinger混音后输出到音频设备。

3,对于视频回放,还会设置setDisplay,或者setSurface

MediaplayerService.cpp执行的是setVideoSurfaceTexture()

Frameworks/av/media/libmediaplayerservice/mediaplayerservice.cpp
status_t MediaPlayerService::Client::setVideoSurfaceTexture(
        const sp<IGraphicBufferProducer>& bufferProducer){
	sp<MediaPlayerBase> p = getPlayer();
//创建本地窗口surface。并将这个surface跟窗口建立连接。
	sp<ANativeWindow> anw;
	anw = new Surface(bufferProducer, true /* controlledByApp */);
	status_t err = nativeWindowConnect(anw.get(), "setVideoSurfaceTexture");
//这里同时传入了buffer的生成者:GraphicBufferProducer。确保断开旧的的连接前执行queue/dequeue不会报错。
	status_t err = p->setVideoSurfaceTexture(bufferProducer);
//记录当前连接到的本地窗口及其IGraphicBufferProducer。
    mConnectedWindow = anw;
    mConnectedWindowBinder = binder;
}

同样要把surface设置到实际的播放器中,记录到mSurface

4,接下来的重点是prepare()的调用。

frameworks/base/media/java/android/media/mediaPlayer.java
public void prepare() throws IOException, IllegalStateException {
//媒体文件,包括音频流,视频流,还可能有字幕流,下面的SubtitleTracks就是字幕流。
	_prepare();
	scanInternalSubtitleTracks();
}

直接看本地层的实现:

Frameworks/base/media/jni/android_media_mediaplayer.cpp
status_t MediaPlayer::prepareAsync_l(){
	mCurrentState = MEDIA_PLAYER_PREPARING;
	return mPlayer->prepareAsync();
}
Frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
void NuPlayer::onMessageReceived(const sp<AMessage> &msg) {
        case kWhatPrepare:        {
            mSource->prepareAsync();
            break;
        }
}

下面会根据datasource的来源是网络,还是本地文件,生成相应的datasource。以本地文件为例:

Frameworks/av/media/libmediaplayerservice/nuplayer/GenericSource.cpp
void NuPlayer::GenericSource::onPrepareAsync() {
//获取MediaExtractorService,用于把原始文件中的数据提取出来交给后面的解码器。
sp<IBinder> binder =
                        defaultServiceManager()->getService(String16("media.extractor"));
	sp<IMediaExtractorService> mediaExService(
                            interface_cast<IMediaExtractorService>(binder));
//如果是本地文件,直接产生DataSource。这里的filesource是datasource的继承类,代表了原始数据,这个原始并不是指磁盘中的,而是从磁盘或者网络读取到内存中的数据,具体是通过readAt方法来读取。
	mDataSource = new FileSource(mFd, mOffset, mLength);
//根据datasource,创建具体的MediaExtractor,视频播放对应的是MPEG4Extractor。MediaExtractor就是容器的解析器,根据MPEG4Extractor得到音视频轨道的mediasource,这个轨道是源数据获取的入口。
	status_t err = initFromDataSource();

if (mVideoTrack.mSource != NULL) {
//从容器提取数据。
        sp<MetaData> meta = doGetFormatMeta(false /* audio */);
        sp<AMessage> msg = new AMessage;
        err = convertMetaDataToMessage(meta, &msg);
        if(err != OK) {
            notifyPreparedAndCleanup(err);
            return;
        }
        notifyVideoSizeChanged(msg);
}
//notify回调通知应用,完成prepared。
finishPrepareAsync();
}

mediaExtractor就是把原始数据提取出来,做音视频的分离,交给音视频轨,也即是audioTrack,videotrack,这个过程中会通过sniff函数来判断文件类型,得到文件类型后,就可以构造具体的mediaExtractor,:Mpeg4Extractor


具体创建mediatractor的地方。

Frameworks/av/media/libstagefright/MediaExtractor.cpp
sp<MediaExtractor> MediaExtractor::CreateFromService(){
//调用sniff函数确定文件格式,
	if (!sniff(source, &tmp, &confidence, &meta)) {}
	mime = tmp.string();

//生成对应的extractor对象。
    MediaExtractor *ret = NULL;
    if ((ret = AVFactory::get()->createExtendedExtractor(source, mime, meta, flags)) != NULL) 	{
        ALOGD("createExtendedExtractor success");
    } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG4)
            || !strcasecmp(mime, "audio/mp4")) {
        ret = new MPEG4Extractor(source);
    } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_MPEG)) {
        ret = new MP3Extractor(source, meta);	
}
…….
}

文件解析,参考:https://blog.csdn.net/cosmoslhf/article/details/17466767


详细看下initFromDataSource函数。

Frameworks/av/media/libmediaplayerservice/nuplayer/GenericSource.cpp
status_t NuPlayer::GenericSource::initFromDataSource() {
//获取具体的MediaExtractor
	extractor = MediaExtractor::Create(mDataSource, NULL,
                mIsStreaming ? 0 : AVNuUtils::get()->getFlags());
//从容器提取数据
	mFileMeta = extractor->getMetaData();
//获取音视频轨,分别设置source。文件解析完,就知道文件中有几路音频,几路视频,将文件中的音视频流,分别存放到audiotrack,videotrack中,
	size_t numtracks = extractor->countTracks();
	for (size_t i = 0; i < numtracks; ++i) {
		sp<IMediaSource> track = extractor->getTrack(i);
		sp<MetaData> meta = extractor->getTrackMetaData(i);
		if (!strncasecmp(mime, "audio/", 6)) {
			mAudioTrack.mSource = track; //实际就是MP3Source
			mAudioTrack.mPackets =
                    new AnotherPacketSource(mAudioTrack.mSource->getFormat());
}else if (!strncasecmp(mime, "video/", 6)) {
	mVideoTrack.mSource = track; //实际就是MPEG4Source
	mVideoTrack.mPackets =
               new  AnotherPacketSource(mVideoTrack.mSource->getFormat());
}
}
}

//最终通过track(MPEG4Source)读取AnotherPacketSource中的数据。AnotherPacketSource就是缓存的数据包,解码器需要的数据将从这里读取。

5,开始媒体播放start()

这个过程最重要的是初始化音视频解码器。中间过程的透传略过,直接看nuPlayer.cpponStart()方法。


NuPlayer.cpp
void NuPlayer::onStart(int64_t startPositionUs, MediaPlayerSeekMode mode) {
//调用renderer,启动解码流程。
    if (mVideoDecoder != NULL) {
        mVideoDecoder->setRenderer(mRenderer);
    }
    if (mAudioDecoder != NULL) {
        mAudioDecoder->setRenderer(mRenderer);
}	
//在kWhatScanSources消息的处理中,初始化解码器。
postScanSources();
}

通过instantiateDecoder()执行解码器初始化。一个可能调用这个函数的地方是在

casekWhatScanSources:这个消息的处理中


NuPlayer.cpp
void NuPlayer::onMessageReceived(const sp<AMessage> &msg) {
	case kWhatScanSources:
//在audio之前,先初始化video decoder,因为video的成功初始化可能改变audio的deep buffer mode,Deep-buffer-pcm格式数据流,缓冲区大小通常容纳大于等于100ms的音频数据,很高的相应延迟,使用它的目的是减小功耗(通常用于屏幕关闭时播放音乐的场景)。
	if (instantiateDecoder(false, &mVideoDecoder) == -EWOULDBLOCK) {}
	if (instantiateDecoder(true, &mAudioDecoder) == -EWOULDBLOCK) {}
}
NuPlayer.cpp
status_t NuPlayer::instantiateDecoder(
        bool audio, sp<DecoderBase> *decoder, bool checkAudioModeChange) {
//获取音频格式
	sp<AMessage> format = mSource->getFormat(audio);
//创建音视频解码器
	if (audio) {
		AVNuUtils::get()->setCodecOutputFormat(format);
		mSource->setOffloadAudio(false /* offload */);
*decoder = AVNuFactory::get()->createDecoder(notify, mSource, mPID, mUID, mRenderer);
}else{
	sp<AMessage> notify = new AMessage(kWhatVideoNotify, this);
	++mVideoDecoderGeneration;
	notify->setInt32("generation", mVideoDecoderGeneration);
	*decoder = new Decoder(
		notify, mSource, mPID, mUID, mRenderer, mSurface, mCCDecoder);
}
//然后执行decoder的初始化和配置实际解码的类。Init函数只是注册了消息机制。
(*decoder)->init();
(*decoder)->configure(format);
}

DecoderBase一个基类,主要提供了AHandlerALooper的消息处理机制,实际的功能实现是在继承类的on***系列的函数中。

NuPlayer中有两个DecoderBase的实现类,一个是NuPlayer::DecoderPassThrough,一个是NuPlayer::Decoder


04-0209:13:49.423 V/NuPlayerDecoder( 544): [video/avc decoder]onConfigure (surface=0xb1363800)

04-0209:13:49.457 V/NuPlayerDecoder( 544): [audio/mp4a-latm decoder]onConfigure (surface=0x0)


Decoder中真正实现解码的类是MediaCodec

NuPlayerDecoder.cpp
void NuPlayer::Decoder::onConfigure(const sp<AMessage> &format) {
//找到音视频的类型。
	AString mime;
	CHECK(format->findString("mime", &mime));
//创建MediaCodec。然后调用MediaCodec的init方法。
	sp<MediaCodec> mCodec;
	mCodec = MediaCodec::CreateByType(
		mCodecLooper, mime.c_str(), false /* encoder */, NULL /* err */, mPid, mUid);
}

对于音频播放来说,mediacodecinit方法会获取一个acodec实例。

Mediacodec.cpp
status_t MediaCodec::init(const AString &name, bool nameIsType, bool encoder) {
//ACodec实际是继承自CodecBase的。这个方法会调用AVFactory的createACodec创建一个ACodec实例。
	sp<CodecBase> mCodec;
	mCodec = GetCodecBase(name, nameIsType);

//这个设置是把new CodecCallback实例记录到Acodec中,以std::unique_ptr<CodecCallback> mCallback;表示,实际是定义在父类CodecBash.h中的。
	mCodec->setCallback(
            std::unique_ptr<CodecBase::CodecCallback>(
                    new CodecCallback(new AMessage(kWhatCodecNotify, this))));
//获取Acodec的ACodecBufferChannel,这个Channel创建了两个AMessage,一个用来填充buffer,一个用来消费填充好的buffer。后期会通过这个BufferCallback发出消息kWhatCodecNotify到MediaCodec这边的onMessageReceived,然后进一步通过AMessage把消息发到NuPlayerDecoder.cpp的onMessageReceived。
    mBufferChannel = mCodec->getBufferChannel();
    mBufferChannel->setCallback(
            std::unique_ptr<CodecBase::BufferCallback>(
                    new BufferCallback(new AMessage(kWhatCodecNotify, this))));
}

ACodec.cpp
std::shared_ptr<BufferChannelBase> ACodec::getBufferChannel() {
    if (!mBufferChannel) {
        mBufferChannel = std::make_shared<ACodecBufferChannel>(
                new AMessage(kWhatInputBufferFilled, this),
                new AMessage(kWhatOutputBufferDrained, this));
    }
    return mBufferChannel;
}

BufferChannel创建的两个Amessage,分别用来filldrain缓冲区。

ACodec.cpp
bool ACodec::BaseState::onMessageReceived(const sp<AMessage> &msg) {
        case kWhatInputBufferFilled:        {
            onInputBufferFilled(msg);
            break;
        }
        case kWhatOutputBufferDrained:        {
            onOutputBufferDrained(msg);
            break;
        }
}

NuPlayerDecoder.cpp
void NuPlayer::Decoder::onMessageReceived(const sp<AMessage> &msg) {
	case kWhatCodecNotify:{
		case MediaCodec::CB_INPUT_AVAILABLE:
			handleAnInputBuffer(index);
		case MediaCodec::CB_OUTPUT_AVAILABLE:
			handleAnOutputBuffer(index, offset, size, timeUs, flags);
}
}

大致列一下跟buffer相关的消息传递,以kWhatInputBufferFilled为例:

std::shared_ptr<BufferChannelBase> ACodec::getBufferChannel() @ ACodec.cpp{
	new AMessage(kWhatInputBufferFilled, this),
}

bool ACodec::BaseState::onMessageReceived(const sp<AMessage> &msg) @ ACodec.cpp {
	case kWhatInputBufferFilled:
		onInputBufferFilled(msg);
}

void ACodec::BaseState::onInputBufferFilled(const sp<AMessage> &msg) @ ACodec.cpp {
	mCodec->mBufferChannel->fillThisBuffer(info->mBufferID);
}

void ACodecBufferChannel::fillThisBuffer(IOMX::buffer_id bufferId)@ 
ACodecBufferChannel.cpp {
	mCallback->onInputBufferAvailable(std::distance(array->begin(), it),
		it->mClientBuffer);
}

void MediaCodec::onInputBufferAvailable() @mediaCodec.cpp{
	sp<AMessage> msg = mCallback->dup();
	msg->setInt32("callbackID", CB_INPUT_AVAILABLE);
}

void NuPlayer::Decoder::onMessageReceived(const sp<AMessage> &msg) 
@NuPlayerDecoder.cpp{
	CHECK(msg->findInt32("callbackID", &cbID));
	case MediaCodec::CB_INPUT_AVAILABLE:
//handleAnInputBuffer的最后会调用onRequestInputBuffers执行下一次解码,同时通过doRequestBuffers来判断是否还有数据需要解码。
		handleAnInputBuffer(index);
}

bool NuPlayer::Decoder::handleAnInputBuffer(size_t index)@ NuPlayerDecoder.cpp {
	最终到了nuplayer这里。
}

在获取到解码后的数据后,交给NuPlayer::Renderer处理:

bool NuPlayer::Decoder::handleAnOutputBuffer(size_t index,
        size_t offset,
        size_t size,
        int64_t timeUs,
        int32_t flags)@NuPlayerDecoder.cpp {
	sp<MediaCodecBuffer> buffer;
	mCodec->getOutputBuffer(index, &buffer);
	mRenderer->queueBuffer(mIsAudio, buffer, reply);
}

到这里,媒体数据就准备就绪了,但是还没能输出到播放设备。


下篇 回放链路创建


发布了183 篇原创文章 · 获赞 53 · 访问量 20万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 精致技术 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览