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
http://blog.csdn.net/dtplayer/article/details/11330343
http://blog.csdn.net/flyingqr/article/details/8576537
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 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