Android8.0平台Camera monkey拷机卡死异常解决方案
程序员文章站
2022-03-23 19:46:26
android8.0平台camera monkey拷机卡死异常
最近在处理一个camera monkey拷机卡死的问题,卡死在停止录像的画面。
monkey测试命令
monkey -p com....
android8.0平台camera monkey拷机卡死异常
最近在处理一个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打开,并跟踪其代码流程如下
238 1538 v audiosource: set stoptime: 80223601 us代码:
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; }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 eos04-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) stopping04-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为空。