android - 在 NDK 中使用 fopen()、fread() 打开文件时出现致命信号 11,但 AAsset_read 工作正常

标签 android c file-io android-ndk native-code

我正在尝试从 SD 卡读取 wav 文件,用于我正在使用 NDK 和 100% native 代码构建的音频采样器应用程序。我有一种方法可以遍历包含样本路径的预定义数组,并将每个样本路径单独流式传输到数据缓冲区中,以便稍后由 OpenSL 播放。

当我将文件作为 Assets 加载时,使用以下方法:

int open_asset(AAssetManager* mgr, char* filename, int samp)
{

    assert(NULL != mgr);
    AAsset *asset = AAssetManager_open(mgr, filename, AASSET_MODE_BUFFER);

    __android_log_write(ANDROID_LOG_DEBUG, "ASSET", "AAssetManager_open");

    if (NULL == asset)
    {
        __android_log_write(ANDROID_LOG_DEBUG, "ASSET", "Asset not found, loading aborted.");
        return JNI_FALSE;
    }

    oneshot_samples[samp].buffer_header = (unsigned short*) malloc(HEADER_SIZE);
    AAsset_read(asset, oneshot_samples[samp].buffer_header, HEADER_SIZE);

    unsigned short* fmttype;
    unsigned long* databytes;

    fmttype = (oneshot_samples[samp].buffer_header + 10);

    if (*fmttype != 0x1)
    {
        __android_log_write(ANDROID_LOG_DEBUG, "ASSET", "*fmttype not PCM, loading aborted.");
        return JNI_FALSE;
    }

    databytes = (oneshot_samples[samp].buffer_header + 20);

    oneshot_samples[samp].data_size = *databytes;

    oneshot_samples[samp].buffer_data = (unsigned short*) malloc(*databytes);
    AAsset_seek(asset, HEADER_SIZE, SEEK_SET);
    AAsset_read(asset, oneshot_samples[samp].buffer_data, oneshot_samples[samp].data_size);

    __android_log_print(ANDROID_LOG_DEBUG, "ASSET", "*fmttype: %x", *fmttype);
    __android_log_print(ANDROID_LOG_DEBUG, "ASSET", "*databytes: %x", *databytes);

    AAsset_close(asset);

    __android_log_write(ANDROID_LOG_DEBUG, "ASSET", "AAsset_close(asset)");

    return JNI_TRUE;
}

它们加载没有问题。但是对于我的应用程序,我希望用户也能够从 SD 卡中读取样本。我将文件路径更改为 SD 卡上的硬编码位置(仅用于测试),并使用 fopen() 和 fread() 尝试了以下方法:

void open_external_file(char* filepath, int samp)
{

    FILE* fp;
    oneshot_samples[samp].buffer_header = (unsigned short*) malloc(HEADER_SIZE);

    __android_log_print(ANDROID_LOG_DEBUG, "open_external_file",
                        "filepath: %s", filepath);

    __android_log_print(ANDROID_LOG_DEBUG, "open_external_file",
                        "size of filepath: %d", sizeof filepath);

    if ((fp = fopen(filepath, "r")) != NULL)
    {
        __android_log_write(ANDROID_LOG_DEBUG, "open_external_file", "fopen()");

        fread(oneshot_samples[samp].buffer_header, sizeof(unsigned short), HEADER_SIZE, fp);
    }

    unsigned short* fmttype;
    unsigned long* databytes;

    fmttype = (oneshot_samples[samp].buffer_header + 10);

    if (*fmttype != 0x1)
    {
        __android_log_write(ANDROID_LOG_DEBUG, "open_external_file", "*fmttype not PCM, loading aborted.");
    }

    databytes = (oneshot_samples[samp].buffer_header + 20);
    oneshot_samples[samp].data_size = *databytes;
    oneshot_samples[samp].buffer_data = (unsigned short*) malloc(*databytes);

    __android_log_print(ANDROID_LOG_DEBUG, "open_external_file", "*fmttype: %x", *fmttype);
    __android_log_print(ANDROID_LOG_DEBUG, "open_external_file", "*databytes: %x", *databytes);


    fseek(fp , HEADER_SIZE , SEEK_SET);
    fread(oneshot_samples[samp].buffer_data, sizeof(unsigned short), oneshot_samples[samp].data_size, fp);

    fclose(fp);
    __android_log_write(ANDROID_LOG_DEBUG, "open_external_file", "fclose(fp);");
}

这给了我可怕的 ANR 和不可避免的崩溃:

06-06 11:42:30.915: I/dalvikvm(7222): threadid=3: reacting to signal 3 06-06 11:42:30.915: A/libc(7222): Fatal signal 11 (SIGSEGV) at 0x0000000c (code=1)

但奇怪的是,它似乎是随机发生的——有 36 个奇数样本,有时错误出现在第 4 个之后,有时在 20 个左右之后。看完这篇thread ,它似乎可能是一个段错误,但我不知道它是如何引起的——或者这两种方法之间的区别是什么。

文件头 *fmttype 和 *databytes 的值每次都 100% 正确返回。

我是 C 的新手,所以我很可能做错了一些明显错误的事情 - 如果更有经验的人能够阐明可能的原因,我将不胜感激。

更新:我使用 AAsset_read() 切换回来,但仍然得到:

06-06 12:33:50.393: I/dalvikvm(9755): threadid=3: reacting to signal 3 06-06 12:33:50.393: I/dalvikvm(9755): Wrote stack traces to '/data/anr/traces.txt'

但文件每次仍能正常加载,应用程序也不会崩溃。对于 512 MB 的测试平板电脑,我的测试样本的总大小为 48.4 MB - 这会导致问题吗?不过,如果是的话,我看不出使用 AAsset_read() 会有什么不同。

最佳答案

您正在分配 HEADER_SIZE字节数:

(unsigned short*) malloc(HEADER_SIZE);

然后阅读HEADER_SIZE * 2字节进入其中,溢出缓冲区:

fread(oneshot_samples[samp].buffer_header, sizeof(unsigned short), HEADER_SIZE, fp);

( fread() 乘以 sizeof(unsigned short) ,加倍。)

您再次对 HEADER_SIZE 进行相同的过度阅读. android Assets 实现不会失败,因为您正在读取正确数量的数据。通过更改 databytes 修复它。至 sizeof(unsigned short) .

无关:如果 fopen() 返回 NULL,您可能应该从函数返回,而不是处理未初始化的数据。

“信号 3”消息仅表示系统正在请求应用程序的堆栈跟踪,可能是因为主线程很忙并且没有响应消息。

关于android - 在 NDK 中使用 fopen()、fread() 打开文件时出现致命信号 11,但 AAsset_read 工作正常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16951680/

相关文章:

android - 构建配置和 Android NDK

java - 安卓:java.lang.NoClassDefFoundError

CS50 pset5 哈希表节点

c++ - 帮助解决这个问题

java - 在 Windows 上的 Java 中检查文件是否为空的最有效方法

android - 从 sqlite 数据库中检索特定的表项

android - 为什么我的评论会出错?

c - 为什么代码打印的字符数与获取的字符数一样多

C++,根据用户确认不断向文件添加记录

java - 在Spring MVC中的Web应用程序的web-inf文件夹中写入txt文件