You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义AMediaDataSource调用AMediaExtractor_setDataSourceCustom报错求助

解决NDK AMediaExtractor自定义AMediaDataSource返回-10000的问题

我之前也碰到过类似的坑,AMEDIA_ERROR_UNKNOWN这个错误在自定义AMediaDataSource时,大概率是回调函数的签名不匹配、实现逻辑有问题,或者初始化细节没做到位导致的。结合你的情况,咱们逐个排查:

1. 严格匹配回调函数的签名

这是最常见的问题,NDK对AMediaDataSource的回调函数签名有严格要求,哪怕返回值类型、参数顺序差一点,系统都无法正确调用这些回调,直接返回未知错误。你需要确保每个回调的签名完全符合NDK定义:

// getSize回调:返回媒体数据总大小
int64_t (*getSize)(void* userdata);

// readAt回调:从offset位置读取size字节到buffer,返回实际读取字节数(0表示EOF,-1表示错误)
ssize_t (*readAt)(void* userdata, off64_t offset, void* buffer, size_t size);

// close回调:释放资源
void (*close)(void* userdata);

// getAvailableSize回调:返回从offset开始可读取的字节数
int64_t (*getAvailableSize)(void* userdata, off64_t offset);

比如如果你把readAt的off64_t写成了int64_t,或者返回值用了int而不是ssize_t,都会导致回调无法被触发。

2. 确保getAvailableSize的实现合法

在API 29中,getAvailableSize是必填的回调,而且返回值不能是负数。如果你的数据源是完整的字节流,直接返回总大小 - offset即可;如果是流式数据源(比如网络流),返回当前可读取的字节数。如果这个函数返回非法值,会直接导致AMediaExtractor初始化失败。

3. 检查初始化流程的完整性

虽然你的代码里已经设置了所有回调,但要确认:

  • 没有把关键回调(比如getSize、readAt)设为nullptr
  • AMediaDataSource_new()和AMediaExtractor_new()都成功返回了非空指针(建议添加空指针检查)

4. 排除模拟器兼容性问题

部分API 29的模拟器对NDK自定义AMediaDataSource的支持有bug,建议你用真实的Pixel 3设备测试一下,看看是否能正常工作。

5. 添加日志调试

哪怕你觉得回调没被调用,也建议在每个回调函数里添加Android日志,确认系统是否真的没有触发它们:

#include <android/log.h>
#define LOG_TAG "MediaDataSource"

int64_t mediaDataSourceGetSize(void* userdata) {
    __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "getSize called");
    // 返回你的数据总大小
    return YOUR_TOTAL_DATA_SIZE;
}

如果日志里看不到这些回调的输出,说明签名肯定有问题;如果能看到,就检查回调的返回值是否合法。

完整的正确配置示例

这里给你一个标准的初始化示例,你可以对照调整:

#include <media/NdkMediaExtractor.h>
#include <android/log.h>

#define LOG_TAG "MediaExtractorDemo"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)

// 自定义数据(如果需要传递给回调,可以在这里定义)
typedef struct {
    // 比如你的自定义字节流指针、总大小等
    uint8_t* data;
    int64_t totalSize;
} CustomData;

int64_t mediaDataSourceGetSize(void* userdata) {
    CustomData* data = (CustomData*)userdata;
    LOGD("getSize called, totalSize: %lld", data->totalSize);
    return data->totalSize;
}

ssize_t mediaDataSourceReadAt(void* userdata, off64_t offset, void* buffer, size_t size) {
    CustomData* data = (CustomData*)userdata;
    LOGD("readAt called, offset: %lld, size: %zu", offset, size);

    if (offset >= data->totalSize) {
        return 0; // EOF
    }

    size_t readSize = size;
    if (offset + readSize > data->totalSize) {
        readSize = data->totalSize - offset;
    }

    memcpy(buffer, data->data + offset, readSize);
    return readSize;
}

void mediaDataSourceClose(void* userdata) {
    CustomData* data = (CustomData*)userdata;
    LOGD("close called");
    // 释放自定义数据资源
    free(data->data);
    free(data);
}

int64_t mediaDataSourceGetAvailableSize(void* userdata, off64_t offset) {
    CustomData* data = (CustomData*)userdata;
    LOGD("getAvailableSize called, offset: %lld", offset);
    if (offset >= data->totalSize) {
        return 0;
    }
    return data->totalSize - offset;
}

void initExtractor() {
    AMediaExtractor *ex = AMediaExtractor_new();
    if (!ex) {
        LOGE("Failed to create AMediaExtractor");
        return;
    }

    // 初始化自定义数据
    CustomData* customData = (CustomData*)malloc(sizeof(CustomData));
    customData->totalSize = YOUR_DATA_SIZE;
    customData->data = (uint8_t*)malloc(YOUR_DATA_SIZE);
    // 填充你的自定义字节流到customData->data

    AMediaDataSource* mediaSource = AMediaDataSource_new();
    if (!mediaSource) {
        LOGE("Failed to create AMediaDataSource");
        AMediaExtractor_delete(ex);
        free(customData->data);
        free(customData);
        return;
    }

    AMediaDataSource_setGetSize(mediaSource, mediaDataSourceGetSize);
    AMediaDataSource_setReadAt(mediaSource, mediaDataSourceReadAt);
    AMediaDataSource_setUserdata(mediaSource, customData);
    AMediaDataSource_setClose(mediaSource, mediaDataSourceClose);
    AMediaDataSource_setGetAvailableSize(mediaSource, mediaDataSourceGetAvailableSize);

    media_status_t status = AMediaExtractor_setDataSourceCustom(ex, mediaSource);
    if (status != AMEDIA_OK) {
        LOGE("Failed to set custom data source: %d", status);
        AMediaDataSource_delete(mediaSource);
        AMediaExtractor_delete(ex);
        free(customData->data);
        free(customData);
        return;
    }

    // 后续操作:获取轨道、配置MediaCodec等
    int trackCount = AMediaExtractor_getTrackCount(ex);
    LOGD("Track count: %d", trackCount);

    // 使用完毕后记得释放资源
    AMediaExtractor_delete(ex);
    // AMediaDataSource会被AMediaExtractor自动释放,不需要手动调用AMediaDataSource_delete
}

总结

优先检查回调函数的签名是否完全匹配NDK的定义,这是最容易出错的点;其次确保每个回调的返回值合法,尤其是getAvailableSize;最后用真实设备验证排除模拟器的问题。

内容的提问来源于stack exchange,提问作者Nitenq

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 06:59:10