最近在处理一个camera monkey拷机卡死的问题,卡死在停止录像的画面。
monkey测试命令
monkey -p com.android.camera2 --throttle 300 --ignore-crashes --ignore-timeouts --ignore-security-exceptions -v -v -v 50000000 &
camera卡住的时候,把mediaserver进程的backtrace打印可发现卡在AudioSource::waitOutstandingEncodingFrames_l函数中,异常LOG:
debugger -b [pid]
...
[04-28 16:35:43]"Binder:237_4" sysTid=2571
[04-28 16:35:43] #00 pc 00018cd8 /system/lib/libc.so (syscall+28)
[04-28 16:35:43] #01 pc 00047529 /system/lib/libc.so (__pthread_cond_timedwait(pthread_cond_internal_t*, pthread_mutex_t*, bool, timespec const*)+102)
[04-28 16:35:43] #02 pc 000a7adb /system/lib/libstagefright.so (android::AudioSource::waitOutstandingEncodingFrames_l()+54)
[04-28 16:35:43] #03 pc 000a787b /system/lib/libstagefright.so (android::AudioSource::reset()+82)
[04-28 16:35:43] #04 pc 000a77e9 /system/lib/libstagefright.so (android::AudioSource::~AudioSource()+44)
[04-28 16:35:43] #05 pc 000a794d /system/lib/libstagefright.so (android::AudioSource::~AudioSource()+12)
[04-28 16:35:43] #06 pc 0000ac5b /system/lib/libutils.so (android::RefBase::decStrong(void const*) const+70)
[04-28 16:35:43] #07 pc 00049c4f /system/lib/libmediaplayerservice.so (android::StagefrightRecorder::~StagefrightRecorder()+206)
[04-28 16:35:43] #08 pc 00049e7b /system/lib/libmediaplayerservice.so (android::StagefrightRecorder::~StagefrightRecorder()+2)
[04-28 16:35:43] #09 pc 00047eef /system/lib/libmediaplayerservice.so (android::MediaRecorderClient::release()+34)
...
把调试LOG打开,并跟踪其代码流程如下
StagefrightRecorder.cpp
status_t StagefrightRecorder::stop() {
int64_t stopTimeUs = systemTime() / 1000;
for (const auto &source : { mAudioEncoderSource, mVideoEncoderSource }) {
if (source != nullptr && OK != source->setStopTimeUs(stopTimeUs)) {
ALOGW("Failed to set stopTime %lld us for %s",
(long long)stopTimeUs, source->isVideo() ? "Video" : "Audio");
}
}
AudioSource.cpp
status_t AudioSource::setStopTimeUs(int64_t stopTimeUs) {
mStopSystemTimeUs = stopTimeUs;
return OK;
}
AudioSource.cpp
if (mStopSystemTimeUs != -1 && timeUs >= mStopSystemTimeUs) {
ALOGV("Drop Audio frame at %lld stop time: %lld us",
(long long)timeUs, (long long)mStopSystemTimeUs);
mNoMoreFramesToRead = true;
mFrameAvailableCondition.signal();
return OK;
}
AudioSource.cpp
status_t AudioSource::read(
MediaBuffer **out, const ReadOptions * /* options */) {
Mutex::Autolock autoLock(mLock);
*out = NULL;
if (mInitCheck != OK) {
ALOGI("read: mInitCheck not OK");
return NO_INIT;
}
while (mStarted && mBuffersReceived.empty()) {
mFrameAvailableCondition.wait(mLock);
if (mNoMoreFramesToRead) {
ALOGI("read: mBuffersReceived is empty and mNoMoreFramesToRead");
return OK;
}
}
此处当mBuffersReceived为空且mNoMoreFramesToRead被设置时候,直接返回.
MediaCodecSource.cpp
case kWhatPull:
status_t err = mSource->read(&mbuf);
if (mbuf != NULL) {
mNotify->post();
msg->post();
} else {
handleEOS();
}
MediaCodecSource.cpp
void MediaCodecSource::Puller::handleEOS() {
ALOGV("puller (%s) posting EOS", mIsAudio ? "audio" : "video");
// android::CallStack cs("Puller::handleEOS");
sp<AMessage> msg = mNotify->dup();
msg->setInt32("eos", 1);
msg->post();
}
因为从source获取到的buffer为空,于是判定为EOS,并进行EOS处理。
MediaCodecSource.cpp
case kWhatStop:
{
ALOGI("encoder (%s) stopping", mIsVideo ? "video" : "audio");
if (mOutput.lock()->mEncoderReachedEOS) {
// if we already reached EOS, reply and return now
ALOGI("encoder (%s) already stopped",
mIsVideo ? "video" : "audio");
(new AMessage)->postReply(replyID);
break;
}
mPuller->stop();
由于先前已经接收到EOS信息,因此MediaCodecSource在处理kWhatStop时候直接返回。
此次问题发生的流程大致如下
暂时的修改方案是
在第4步处理kWhatStop消息时候,即使已经进行过EOS处理,仍然执行Puller.stop,以确保Puller::Queue.mReadBuffers为空。