分析过程:将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->reachedEOSbool 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()条件满足了,这里两者都为0mOffsetVector赋值的地方也就是在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.cppstatus_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()这个判断条件的。