本文将带你了解Android应用开发Android8.0平台Camera monkey拷机卡死异常解决方案,希望本文对大家学Android有所帮助。
Android8.0平台Camera monkey拷机卡死异常
最近在处理一个camera monkey拷机卡死的问题,卡死在停止录像的画面。
monkey测试命令
?1monkey -p com.android.camera2 --throttle 300 --ignore-crashes --ignore-timeouts --ignore-security-exceptions -v -v -v 50000000 &
camera卡住的时候,把mediaserver进程的backtrace打印可发现卡在AudioSource::waitOutstandingEncodingFrames_l函数中,异常LOG:
?1234567891011121314debugger -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打开,并跟踪其代码流程如下
238 1538 V AudioSource: Set stoptime: 80223601 us
代码:
StagefrightRecorder.cppstatus_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.cppstatus_t AudioSource::setStopTimeUs(int64_t stopTimeUs) { mStopSystemTimeUs = stopTimeUs; return OK;}
238 1523 V AudioSource: dataCallbackTimestamp: 80264993 us
238 1523 V AudioSource: Drop Audio frame at 80264993 stop time: 80223601 us
代码:
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; }
238 1538 I AudioSource: read: mBuffersReceived is empty and mNoMoreFramesToRead
代码
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被设置时候,直接返回.
04-30 07:57:53.687 238 1538 V MediaCodecSource: puller (audio) posting EOS
04-30 07:57:53.730 238 1513 V MediaCodecSource: puller (audio) reached EOS
04-30 07:57:53.730 238 1513 V MediaCodecSource: encoder (audio) reached EOS
代码
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 msg = mNotify->dup(); msg->setInt32("eos", 1); msg->post(); }
因为从source获取到的buffer为空,于是判定为EOS,并进行EOS处理。
04-30 07:57:54.126 238 1513 I MediaCodecSource: encoder (audio) stopping
04-30 07:57:54.126 238 1513 I MediaCodecSource: encoder (audio) already stopped
代码
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时候直接返回。
此次问题发生的流程大致如下
应用层发出停止录像的请求 AudioSource把该停止录像的请求之后的音频数据丢弃,并设置mNoMoreFramesToRead MediaCodecSource从AudioSource读取到buffer为NULL,于是判定为EOS,并进行相关处理,但并不清除MediaCodecSource::Puller::Queue.mReadBuffers MediaCodecSource接收到kWhatStop消息,发现已经进行了EOS处理,于是放弃执行Puller.stop(它会对Puller::Queue.mReadBuffers执行flush操作),直接返回。 AudioSource最后进行reset时候,调用waitOutstandingEncodingFrames_l去等待直到所有buffer被释放,但有些buffer在Audio Puller::Queue.mReadBuffers没有释放,于是在进程被hold在此处。
暂时的修改方案是
在第4步处理kWhatStop消息时候,即使已经进行过EOS处理,仍然执行Puller.stop,以确保Puller::Queue.mReadBuffers为空。
本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注职坐标移动开发之Android频道!