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

使用Android NDK Camera API编码视频出现画面全绿问题求助

问题排查与修复方案

核心问题分析

录制视频画面全绿的本质是YUV420数据的UV分量未正确传递给编码器,解码器会将缺失的UV通道默认填充为0(对应视觉上的绿色)。结合你的代码,主要存在以下几个关键问题:


具体修复点

1. 完整拷贝YUV三个平面的数据

你当前仅拷贝了Y平面(索引0),但YUV420_888格式包含Y、U、V三个独立平面,必须全部传入编码器。修改image_reader_on_image_available中的数据拷贝逻辑:

// 需要确保width、height为全局可访问变量,或从上下文传入
extern int32_t width, height;

if (inputBufferIdx >= 0) {
    LOGI("dequeue input buffer (size: %u)", bufferSize);
    int dataSizeTotal = 0;

    // 处理Y平面(索引0)
    uint8_t* yData = NULL;
    int ySize = 0;
    int yRowStride = 0;
    AImage_getPlaneData(image, 0, &yData, &ySize);
    AImage_getPlaneRowStride(image, 0, &yRowStride);
    // 逐行拷贝Y数据(stride可能大于width,避免冗余数据)
    for (int i = 0; i < height; i++) {
        memcpy(inputBuffer + i * width, yData + i * yRowStride, width);
    }
    dataSizeTotal += height * width;

    // 处理U平面(索引1)
    uint8_t* uData = NULL;
    int uSize = 0;
    int uRowStride = 0;
    int uPixelStride = 0;
    AImage_getPlaneData(image, 1, &uData, &uSize);
    AImage_getPlaneRowStride(image, 1, &uRowStride);
    AImage_getPixelStride(image, 1, &uPixelStride);
    int uHeight = (height + 1) / 2;
    int uWidth = (width + 1) / 2;
    // 拷贝U平面数据
    for (int i = 0; i < uHeight; i++) {
        for (int j = 0; j < uWidth; j++) {
            inputBuffer[dataSizeTotal + i * uWidth + j] = uData[i * uRowStride + j * uPixelStride];
        }
    }
    dataSizeTotal += uHeight * uWidth;

    // 处理V平面(索引2)
    uint8_t* vData = NULL;
    int vSize = 0;
    int vRowStride = 0;
    int vPixelStride = 0;
    AImage_getPlaneData(image, 2, &vData, &vSize);
    AImage_getPlaneRowStride(image, 2, &vRowStride);
    AImage_getPixelStride(image, 2, &vPixelStride);
    int vHeight = (height + 1) / 2;
    int vWidth = (width + 1) / 2;
    // 拷贝V平面数据
    for (int i = 0; i < vHeight; i++) {
        for (int j = 0; j < vWidth; j++) {
            inputBuffer[dataSizeTotal + i * vWidth + j] = vData[i * vRowStride + j * vPixelStride];
        }
    }
    dataSizeTotal += vHeight * vWidth;

    int64_t timestamp = 0;
    AImage_getTimestamp(image, &timestamp);
    // 传入实际总数据大小,而非缓冲区总大小
    AMediaCodec_queueInputBuffer(mediaCodec, inputBufferIdx, 0, dataSizeTotal, timestamp, 0);
}

2. 修正编码器ColorFormat配置

COLOR_FormatYUV420Flexible为兼容格式,部分设备编码器对其支持不佳,建议改用明确的平面格式:

// 替换原ColorFormat设置
AMediaFormat_setInt32(mediaFormat, AMEDIAFORMAT_KEY_COLOR_FORMAT, 
                      AMEDIACODEC_COLOR_FormatYUV420Planar);

3. 修复输入缓冲区参数错误

你之前调用AMediaCodec_queueInputBuffer时传入的bufferSize是编码器输入缓冲区的总大小,但实际拷贝的数据仅为Y平面大小,会导致编码器读取无效数据。改用上述代码中计算的dataSizeTotal作为实际数据长度。

4. 验证时间戳正确性

确认AImage_getTimestamp返回的时间戳单位为微秒,若单位不符需转换,避免编码器时序错误导致画面异常。


额外适配建议

  • 若编码器配置失败,可枚举当前设备支持的编码器格式进行适配:
    AMediaCodecInfo* codecInfo = AMediaCodec_createCodecInfoByName("OMX.google.h264.encoder");
    const AMediaFormat* const* formats = AMediaCodecInfo_getSupportedInputFormats(codecInfo);
    for (int i = 0; formats[i]; i++) {
        LOGI("Supported input format: %s", AMediaFormat_toString(formats[i]));
    }
    AMediaCodecInfo_delete(codecInfo);
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 15:06:17