android 多媒体框架

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lf12345678910/article/details/52840057

http://blog.csdn.net/matrix_laboratory/article/details/39322749

mediaplayer:

ImediaPlayer:BnMediaPlayer(MediaPlayerService.)、BpMediaPlayer(ImediaPlayer)

MediaPlayerClient:BnMediaPlayerClient(MediaPlayer)、BpMediaPlayerClient(IMediaPlayerClient)

IMediaPlayerService:BnMediaPlayerService(MediaPlayerService)、BpMediaPlayerService(IMediaPlayerService)

StagefrightPlayer架构:

AudioPlayer.cpp     AweSomePlayer.cpp                                                                                                                                          (notifyListener_l(MEDIA_SEEK_COMPLETE);)

AudioCallBack  -->notifyAudioEOS--->postAudioEOS--->postCheckAudioStatusEvent--->onCheckAudioStatus--->postStreamDoneEvent_l-->onStreamDone-->notifyListener_l

MediaPlayerInterface      StagefrightPlayer.cpp   MediaPlayerFactory   MediaPlayerService                      (IMediaPlayerClient)MediaPlayer   

--->sendEvent------------->(setNotifyCallback)--->(createPlayer)--------->(createPlayer)---------->notify---->notify---->

android_media_MediaPlayer.cpp(JNIMediaPlayerListener: public MediaPlayerListener)                                           MediaPlayer.java

-->env->CallStaticVoidMethod(fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative",))----->postEventFromNative--->EventHandler

NuPlayer架构:

ACodec.cpp             NuPlayerDecoder.cpp      MediaCodec.cpp                                                                                 

NuPlayerDecoder.cpp                                               NuPlayerRenderer.cpp                      

 (kWhatCodecNotify)           ("callback", &callback)              ("callbackID", CB_OUTPUT_AVAILABLE)     (MediaCodec::CB_OUTPUT_AVAILABLE)                                                        (kWhatOutputBufferDrained)                                    

onMessageReceived(BaseState)-->onOMXMessage-->(MediaCodec:mCodec->setNotificationMessage(kWhatCodecNotify))-->onOMXFillBufferDone--->{onConfigure(setCallback)--->setCallback--->onMessageReceived(kWhatSetCallback)}-->onMessageReceived(kWhatDrainThisBuffer)-->onOutputBufferAvailable--->onMessageReceived(kWhatCodecNotify)-->handleAnOutputBuffer-->queueEOS-->onQueueEOS-->postDrainAudioQueue_l--

>onMessageReceived(kWhatDrainAudioQueue)-->onDrainAudioQueue(notifyEOS) --

(NuPlayerRenderer(mAudioSink, notify, flags);)                                                              NuPlayer.cpp                                  

>onDrainAudioQueue-->notifyEOS(kWhatRendererNotify  :"what", kWhatEOS)--> onMessageReceived(kWhatRendererNotify : notifyListener) ---

                                                                  NuPlayerDriver.cpp

> notifyListener(notifyListener_l) --->sendEvent-->

MediaPlayerInterface      NuPlayerDriver.cpp   MediaPlayerFactory   MediaPlayerService                      (IMediaPlayerClient)MediaPlayer   

--->sendEvent------------->(setNotifyCallback)--->(createPlayer)--------->(createPlayer)---------->notify---->notify---->

stagefright多媒体框架处理流程

http://3y.uu456.com/bp_3wh8b8kbu33uh255bml9_2.html

http://www.kancloud.cn/digest/androidframework/128563

【stagefrightplayer】3 MediaExtractor介绍

http://blog.csdn.net/dtplayer/article/details/11330343

stagefright 架构分析(四) MediaExtractor

http://blog.csdn.net/flyingqr/article/details/8576537

Android Multimedia框架总结(八)Stagefright框架之AwesomePlayer及数据解析器

http://blog.csdn.net/hejjunlin/article/details/52503057

问题背景:

dlna测试,有一项测试用例是用不同的服务器共享adts文件,我们的手机作为终端用dlna应用去播放这些文件,都是采用的http形式的流媒体协议播放,在测试中发现一个avox服务器共享的adts文件无法播放

分析过程:

将Awesomeplayer中的log开关打开,发现刚准备好播放的时候,下面的log就打出来了

ALOGV("MEDIA_PLAYBACK_COMPLETE");

那下面就以这个为线索进行跟踪问题原因了

voidAwesomePlayer::onStreamDone() {

    // Posted whenever anystream finishes playing.

    if ((mFlags& LOOPING) {

       ......

    } else {

       ALOGV("MEDIA_PLAYBACK_COMPLETE");

       notifyListener_l(MEDIA_PLAYBACK_COMPLETE);

       pause_l(true );

       modifyFlags(AT_EOS, SET);

    }

}

voidAwesomePlayer::postStreamDoneEvent_l(status_t status){

    mStreamDoneStatus =status;

    mQueue.postEvent(mStreamDoneEvent);

}

postStreamDoneEvent_l有两个地方发出:

第一次在onVideEvent里面,因为是音频,应当都不会进入这里面

第二次是在下面这个函数中,这个函数作为一个事件,用来不断检查音频文件是否播放完成

void AwesomePlayer::onCheckAudioStatus() {

{

    Mutex::AutolockautoLock(mAudioLock);

    status_tfinalStatus;

   //mWatchForAudioEOS这个变量肯定为true

    //mAudioPlayer->reachedEOS(&finalStatus)这个条件满足了导致

    if (mWatchForAudioEOS&& mAudioPlayer->reachedEOS(&finalStatus)){

       mWatchForAudioEOS = false;

       modifyFlags(AUDIO_AT_EOS, SET);

       modifyFlags(FIRST_FRAME, SET);

       postStreamDoneEvent_l(finalStatus);

    }

}

下面跟踪mAudioPlayer->reachedEOS

bool AudioPlayer::reachedEOS(status_t *finalStatus) {

    *finalStatus = OK;

    Mutex::AutolockautoLock(mLock);

    *finalStatus =mFinalStatus;

    returnmReachedEOS;

}

从结果来看,这里返回的mReachedEOS肯定为true,而且finalStatus为ERROR_END_OF_STREAM

在AudioPlayer中搜索设置mReachedEOS为true的地方,找到如下的地方:

size_t AudioPlayer::fillBuffer(void *data, size_t size){

    if (mNumFramesPlayed ==0) {

       ALOGV("AudioCallback");

    }

    if (mReachedEOS) {

       return 0;

    }

    bool postSeekComplete =false;

    bool postEOS =false;

    int64_t postEOSDelayUs =0;

    size_t size_done =0;

    size_t size_remaining =size;

    while (size_remaining> 0) {

       if (mInputBuffer == NULL) {

           status_terr;

           //下面的ifelse需要确定是走哪个分支

           if(mIsFirstBuffer) {

              mInputBuffer =mFirstBuffer;

              mFirstBuffer = NULL;

              err =mFirstBufferResult;

              mIsFirstBuffer = false;

           } else{

              err =mSource->read(&mInputBuffer,&options);

           }

           CHECK((err== OK && mInputBuffer !=NULL)

                || (err != OK &&mInputBuffer == NULL));

          Mutex::Autolock autoLock(mLock);

          mReachedEOS = true;

          mFinalStatus = err;

          break;

           }

也就是在调用mSource->read(&mInputBuffer,&options)时出错

下面是视频播放中一系列read的调用关系:

audioplayer回调函数---fillBuffer---AudioPlayer::read()----OMXCodec::read()----各个分离器Source::read()(这里是AACSource::read())

省略中间的过程,直接定位到AACExtractor.cpp这个文件中的read()函数:

发现有这么一段代码:

size_t frameSize, frameSizeWithoutHeader, headerSize;

if ((seekFrame >= mOffsetVector.size()) ||(frameSize = getAdtsFrameLength(mDataSource, mOffset,&headerSize)) == 0) {

       return ERROR_END_OF_STREAM;

}

打log发现,程序进入了这个if分支,也就是出现问题的原因了,下面就要分析为什么会进入这个if分支,由于||是断路操作符,第一个条件满足了就没有执行后面的

也就是seekFrame >=mOffsetVector.size()条件满足了,这里两者都为0

mOffsetVector赋值的地方也就是在AACExtractor的构造函数中,构造函数中有非常关键的下面的代码:

    if(mDataSource->getSize(&streamSize)== OK) {

        while (offset <streamSize) {

           if((frameSize = getAdtsFrameLength(source, offset, NULL)) == 0){

              return;

           }

          mOffsetVector.push(offset);

           offset +=frameSize;

           numFrames++;

       }

       // Round up and get the duration

       mFrameDurationUs = (1024 * 1000000ll + (sr - 1))/ sr;

       duration = numFrames * mFrameDurationUs;

       mMeta->setInt64(kKeyDuration,duration);

    }

这个if分支里面有个while循环用来计算帧数,并且最后计算这个文件的时长,打log发现,出问题的服务器上并没有进入这个if条件,而正常的服务器进入了。

分析if中的条件:

mDataSource->getSize(&streamSize) ==OK

跟踪一下这个代码:mDataSource是NuCacheSource2.cpp

status_t NuCachedSource2::getSize(off64_t *size) {

    returnmSource->getSize(size);  //mSource是ChromiumHTTPDataSource.cpp

}

status_t ChromiumHTTPDataSource::getSize(off64_t *size){

    Mutex::AutolockautoLock(mLock);

    if(mContentSize < 0) {

       return ERROR_UNSUPPORTED;

    }

    *size =mContentSize;

    return OK;

}

由于这个方法没有返回ok,也就是mContentSize 小于0了

赋值的地方就一处:

void ChromiumHTTPDataSource::onConnectionEstablished(

       int64_t contentSize, const char *contentType){

    mState =CONNECTED;

    mContentSize = (contentSize < 0) ? -1 :contentSize + mCurrentOffset;

    mContentType =String8(contentType);

   mCondition.broadcast();

}

这个函数是在服务器连接上的回调中调用的,在support.cpp中调用,具体看一下代码

void SfDelegate::OnResponseStarted(net::URLRequest *request){

   MY_LOGV("OnResponseStarted");

    std::stringheaders;

   request->GetAllResponseHeaders(&headers);

   MY_LOGV(StringPrintf("response headers: %s",headers.c_str()).c_str());

    std::stringcontentType;

   request->GetResponseHeaderByName("Content-Type",&contentType);

   mOwner->onConnectionEstablished(

          request->GetExpectedContentSize(),contentType.c_str());

}

request->GetExpectedContentSize()用来从服务器发送的报文中获取文件的大小,也就是这里没有获取到,导致后面没有设置成功。

分析到这里,就自然而然的想到去抓包分析服务器返回的报文,抓取ip log发现:

正常情况的如下,发送的报文中有如下信息:

content-length: 2102023

而不正常的情况,采用的是另一套标准,发送如下信息:

transfer-encoding:chunked

这种方式没有具体返回文件的长度,综上也就是最终导致问题出现的原因。

其实谷歌原始代码中是没有seekFrame >=mOffsetVector.size()这个判断条件的。

-------------------------------------------------------------------------------------------

http://blog.csdn.net/mandagod/article/details/47207511

10-19 14:41:29.891   245  1526 V MediaPlayerFactory:  create NuPlayer

猜你喜欢

转载自blog.csdn.net/lf12345678910/article/details/52840057