【问题标题】:Android weird AudioTrack crashAndroid 奇怪的 AudioTrack 崩溃
【发布时间】:2018-03-06 07:43:12
【问题描述】:

我是一名 Android 应用程序开发人员,我遇到以下崩溃报告(我每天收到 5/6):

  native: pc 00000000000418e0  /system/lib/libc.so (tgkill+12)
  native: pc 0000000000040d59  /system/lib/libc.so (pthread_kill+32)
  native: pc 000000000001c7eb  /system/lib/libc.so (raise+10)
  native: pc 000000000001999d  /system/lib/libc.so (__libc_android_abort+34)
  native: pc 0000000000017550  /system/lib/libc.so (abort+4)
  native: pc 0000000000008d53  /system/lib/libcutils.so (__android_log_assert+86)
  native: pc 000000000006e2c3  /system/lib/libmedia.so (_ZN7android11ClientProxy13releaseBufferEPNS_5Proxy6BufferE+94)
  native: pc 000000000006c11d  /system/lib/libmedia.so (_ZN7android10AudioTrack13releaseBufferEPKNS0_6BufferE+112)
  native: pc 000000000006c987  /system/lib/libmedia.so (_ZN7android10AudioTrack18processAudioBufferEv+1350)
  native: pc 000000000006d7f3  /system/lib/libmedia.so (_ZN7android10AudioTrack16AudioTrackThread10threadLoopEv+194)
  native: pc 0000000000010079  /system/lib/libutils.so (_ZN7android6Thread11_threadLoopEPv+112)
  native: pc 000000000004065b  /system/lib/libc.so (_ZL15__pthread_startPv+30)
  native: pc 000000000001a021  /system/lib/libc.so (__start_thread+6)

OpenSL 函数在 JNI 世界中调用。

这些是堆中存储的所有变量:

/* OpenSL ES audio stuff */
SLObjectItf engineObject = NULL;
SLEngineItf engineEngine = NULL;
SLObjectItf outputMixObject = NULL;
SLObjectItf playerObject = NULL;
SLPlayItf   playerPlay = NULL;
SLVolumeItf playerVolume = NULL;
SLAndroidSimpleBufferQueueItf playerBufferQueue = NULL;
char        openSLinited = 0;

int16_t     audioBuffer1[48000];
int16_t     audioBuffer2[48000];
int16_t    *currentAudioBuffer;

这就是我初始化所有机器的方式:

void Java_com_myapp_myappname_MyActivity_jniOpenSLInit(JNIEnv *env,
                                                       jobject thiz,
                                                       jint freq)
{
    SLresult result;

    // create engine
    result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);
    assert(SL_RESULT_SUCCESS == result);

    // realize the engine
    result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
    assert(SL_RESULT_SUCCESS == result);

    // get the engine interface, which is needed to create other objects
    result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE,
                                           &engineEngine);
    assert(SL_RESULT_SUCCESS == result);

    // create output mix
    result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject,
                                              0, NULL, NULL);
    assert(SL_RESULT_SUCCESS == result);

    // realize the output mix
    result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
    assert(SL_RESULT_SUCCESS == result);

    SLuint32 SLfreq;
    if (freq == 44100)
        SLfreq = SL_SAMPLINGRATE_44_1;
    else
        SLfreq = SL_SAMPLINGRATE_48;

    SLDataLocator_AndroidSimpleBufferQueue loc_bufq =
        {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2};
    SLDataFormat_PCM format_pcm = {SL_DATAFORMAT_PCM, 2, SLfreq,
                                   SL_PCMSAMPLEFORMAT_FIXED_16,
                                   SL_PCMSAMPLEFORMAT_FIXED_16,
                                   SL_SPEAKER_FRONT_LEFT |
                                   SL_SPEAKER_FRONT_RIGHT,
                                   SL_BYTEORDER_LITTLEENDIAN};

    SLDataSource audioSrc = {&loc_bufq, &format_pcm};

    /* configure audio sink */
    SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX,
                                          outputMixObject};
    SLDataSink audioSnk = {&loc_outmix, NULL};

    const SLInterfaceID idsAudioPlayer[2] = {SL_IID_BUFFERQUEUE,
                                             SL_IID_VOLUME };

    const SLboolean reqAudioPlayer[2] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE };

    result = (*engineEngine)->CreateAudioPlayer(engineEngine, &playerObject,
                                                &audioSrc,
                                                &audioSnk, 2, idsAudioPlayer,
                                                reqAudioPlayer);
    assert(SL_RESULT_SUCCESS == result);

    // realize the player
    result = (*playerObject)->Realize(playerObject, SL_BOOLEAN_FALSE);
    assert(SL_RESULT_SUCCESS == result);

    // get the play interface
    result = (*playerObject)->GetInterface(playerObject, SL_IID_PLAY,
                                           &playerPlay);
    assert(SL_RESULT_SUCCESS == result);

    // get the volume interface
    result = (*playerObject)->GetInterface(playerObject, SL_IID_VOLUME,
                                           &playerVolume);
    assert(SL_RESULT_SUCCESS == result);

    // get the buffer queue interface
    result = (*playerObject)->GetInterface(playerObject, SL_IID_BUFFERQUEUE,
                                           &playerBufferQueue);
    assert(SL_RESULT_SUCCESS == result);

    // register callback on the buffer queue
    result = (*playerBufferQueue)->RegisterCallback(playerBufferQueue,
                                                    audio_player_cb, NULL);
    assert(SL_RESULT_SUCCESS == result);

    // done!
    openSLinited = 1;
}

调用它来启动机器。

void openSLStart()
{
    bzero(audioBuffer1, 96000);
    bzero(audioBuffer2, 96000);

    if (!openSLinited)
        return;

    (*playerBufferQueue)->Enqueue(playerBufferQueue, audioBuffer1,
                                  4096 * 4);
    (*playerBufferQueue)->Enqueue(playerBufferQueue, audioBuffer2,
                                  4096 * 4);

    currentAudioBuffer = audioBuffer1;

    // set the player's state to playing
    (*playerPlay)->SetPlayState(playerPlay, SL_PLAYSTATE_PLAYING);
}

这是让新样本入队的回调

void audio_player_cb(SLAndroidSimpleBufferQueueItf bq, void *context)
{
    if (!openSLinited)
        return;

    assert(bq == playerBufferQueue);
    assert(NULL == context);

    // switch between audio buffer 1 and 2
    if (currentAudioBuffer == audioBuffer1)
        currentAudioBuffer = audioBuffer2;
    else
        currentAudioBuffer = audioBuffer1;

    // this function read samples (4096 16 bit samples) from an internal buffer
    sound_read_samples(4096, currentAudioBuffer);

    // feed openSL machine
    (*playerBufferQueue)->Enqueue(playerBufferQueue, currentAudioBuffer,
                                  4096 * 2);
}

最后,这就是 OpenSL 的终止方式

void Java_com_myfirm_myappname_MyActivity_jniOpenSLTerm(JNIEnv *env,
                                                        jobject thiz)
{
    // shutdown every created object
    if (playerObject)
    {
        // stop the player
        SLresult result = (*playerPlay)->SetPlayState(playerPlay,
                                                      SL_PLAYSTATE_STOPPED);

        if (SL_RESULT_SUCCESS == result)
            utils_log("Player succesfully stopped");

        (*playerObject)->Destroy(playerObject);
        playerObject = NULL;
        playerPlay = NULL;
    }

    if (outputMixObject)
    {
        (*outputMixObject)->Destroy(outputMixObject);
        outputMixObject = NULL;
    }

    if (engineObject)
    {
        (*engineObject)->Destroy(engineObject);
        engineObject = NULL;
    }

    openSLinited = 0;

    utils_log("OpenSLTerm complete");
}

我无法在我的手机和模拟器上重现...它永远不会以这种方式崩溃。

我没有关于如何解决此问题的想法。有人可以帮我摆脱这个崩溃吗?

10 月 8 日更新

我尝试按照建议删除日志。崩溃仍然发生。

该问题影响 Android 6.0、7.0 和 7.1(至少,我没有收到不同版本的报告)

10 月 9 日更新

应阿姆贾德汗的要求:

这是生成库的 Android.mk

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := libmyapp-jni
LOCAL_SRC_FILES := src.c src2.c src3.c 
LOCAL_LDLIBS := -llog -landroid -ljnigraphics -lGLESv2 -lOpenSLES
LOCAL_CFLAGS += -O3 -DNDEBUG

include $(BUILD_SHARED_LIBRARY)

这是构建库的命令(生成所有 ABI)

/cygdrive/c/Android/ndk/ndk-build.cmd NDK_DEBUG=0 APP_BUILD_SCRIPT=./Android.mk NDK_PROJECT_PATH=. 

提前谢谢你。

【问题讨论】:

  • 由于在 Nougat 和更高版本中出现此错误,请尝试从您的本机 .so 文件中删除所有日志,我们正在使用本机 so 文件中的日志,这也会在 playstore 中生成此崩溃。 (utils_log)这个方法,你可以把[assert()]代码
  • 我试图删除日志...崩溃再次发生。似乎它会影响 android 6、7.0 和 7.1。我真的迷路了
  • 您能否添加您的 Android.mk 代码以及 ABI 将支持的代码
  • 你可以发布你的 Application.mk...

标签: android audio opensl


【解决方案1】:

我已经看到了崩溃,它与我的崩溃是由于 android 日志而产生的,最近我上传了 apk 并找到了相同的踪迹。

  native: pc 0000000000048793  /system/lib/libc.so (pthread_kill+34)
  native: pc 000000000001d5d5  /system/lib/libc.so (raise+10)
  native: pc 0000000000019111  /system/lib/libc.so (__libc_android_abort+34)
  native: pc 0000000000017174  /system/lib/libc.so (abort+4)
  native: pc 000000000000c481  /system/lib/libcutils.so (__android_log_assert+112)
  native: pc 0000000000025595  /system/lib/libhwui.so
  native: pc 00000000000270d1  /system/lib/libhwui.so
  native: pc 000000000002b959  /system/lib/libhwui.so (_ZN7android10uirenderer12renderthread12RenderThread10threadLoopEv+80)
  native: pc 000000000000e35d  /system/lib/libutils.so (_ZN7android6Thread11_threadLoopEPv+140)
  native: pc 000000000006830b  /system/lib/libandroid_runtime.so (_ZN7android14AndroidRuntime15javaThreadShellEPv+102)
  native: pc 0000000000048263  /system/lib/libc.so (_ZL15__pthread_startPv+22)
  native: pc 0000000000019b5d  /system/lib/libc.so (__start_thread+6)

这个回溯是我最近的更新,所以我在我的原生文件中打印 android 日志。

__android_log_print(ANDROID_LOG_ERROR, "TRACKERS", "%s", Str);

我正在使用日志打印我的数据以检查我上传的时间我得到了那个回溯,所以我已经从本机和从本机调用的 java 函数中删除了所有日志。

然后,在我完成了干净的构建并上传之后,所以在最新的构建中它直到现在才生成超过 5 个构建上传

Android 特定日志支持

包含应用程序可以用来从本机代码向内核发送日志消息的各种定义。有关这些定义的更多信息,请参阅 中的 cmets。

您可以编写自己的包装宏来访问此功能。如果您希望执行日志记录,您的本机模块应链接到 /system/lib/liblog.so。通过在您的 Android.mk 文件中包含以下行来实现此链接:

LOCAL_LDLIBS := -llog

在这里你会找到轻松找出原生错误的方法

【讨论】:

  • 我已经完全删除了所有的日志。崩溃仍然发生。
  • 您是在尝试为 64 位架构构建 so 文件,还是为 64 位架构使用外部库?
  • 我已经为每个架构构建了库。崩溃发生在 32 位或 64 位中
  • @Davide Berra K 仅使用 32 位架构,它会自动使用 64 位,我将编辑答案
【解决方案2】:

这看起来像是 OpenSL ES 中的一个已知错误。 插入耳机后,您可以在 releaseBuffer() 中获取断言。 如果回调具有高 CPU 负载,则会出现更严重的竞争条件。

回调从调用 AudioTrack 中的 gainBuffer 开始。 当回调结束时,它会调用 releaseBuffer。但如果 设备在回调中间发生变化,然后它不能将缓冲区释放到原始源。所以它断言。

如果您从回调中调用 playInterface->getPosition(),则更有可能发生此崩溃,因为这会触发任何挂起的设备切换。

在双簧管网站上的技术说明中有完整的分析。 https://github.com/google/oboe/blob/master/docs/notes/rlsbuffer.md

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-11-16
    • 2019-03-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-01
    相关资源
    最近更新 更多