欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

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 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为空。