# AppSuitExt **Repository Path**: codefarmer_admin/app-suit-ext ## Basic Information - **Project Name**: AppSuitExt - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-05-11 - **Last Updated**: 2025-05-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README 你提到的 RGBA 字节序与 libyuv 函数匹配问题 是关键所在。 虽然 Android 的 PixelFormat.RGBA_8888 在概念上表示 R,G,B,A 的顺序, 但实际内存中的字节排列可能因设备而异。若使用 RGBAToI420 颜色错误,而 ABGRToI420 正确, 说明你的设备采用了 小端序(ABGR 内存布局)。 一、RGBA 字节序详解 1. 概念顺序 vs 内存顺序 概念顺序(RGBA):颜色通道的逻辑顺序为 红→绿→蓝→透明度。 内存顺序:实际存储时,字节可能按 A,B,G,R 排列(小端序),尤其是在基于 x86 或 ARM 的设备上。 2. 验证方法 通过 JNI 打印前 4 个像素的字节值,确认内存布局: // 在 JNI 层添加调试代码 LOGD("RGBA 内存布局验证:"); for (int i = 0; i < 4; i++) { // 检查前 4 个像素 int offset = i * 4; LOGD("像素 #%d: 字节0=0x%02X, 字节1=0x%02X, 字节2=0x%02X, 字节3=0x%02X", i, static_cast(rgbaBytes[offset]), // 第1个字节 static_cast(rgbaBytes[offset+1]), // 第2个字节 static_cast(rgbaBytes[offset+2]), // 第3个字节 static_cast(rgbaBytes[offset+3])); // 第4个字节 } 3. 输出分析 若为 RGBA 布局: 纯红色像素应显示为 0xFF,0x00,0x00,0xFF(R=255,G=0,B=0,A=255)。 若为 ABGR 布局: 纯红色像素会显示为 0xFF,0x00,0x00,0xFF(A=255,B=0,G=0,R=255)。 二、libyuv 函数选择 根据你的设备情况,应使用 ABGRToI420 而非 RGBAToI420,因为: RGBAToI420 期望输入的内存顺序为 R,G,B,A(大端序)。 ABGRToI420 匹配实际的 A,B,G,R 内存布局(小端序)。 修正后的 JNI 代码: extern "C" JNIEXPORT jint JNICALL Java_com_green_screenrecorder_utils_NativeUtils_rgbaToI420(JNIEnv *env, jclass clazz, jbyteArray rgbaData, jint rgbaStride, jbyteArray i420Data, jint width, jint height) { jbyte* rgbaBytes = env->GetByteArrayElements(rgbaData, nullptr); jbyte* i420Bytes = env->GetByteArrayElements(i420Data, nullptr); if (!rgbaBytes || !i420Bytes) return -1; // 计算 I420 各平面 uint8_t* y_plane = reinterpret_cast(i420Bytes); uint8_t* u_plane = y_plane + width * height; uint8_t* v_plane = u_plane + (width * height) / 4; // 使用 ABGRToI420 匹配实际的 ABGR 内存布局 int result = libyuv::ABGRToI420( reinterpret_cast(rgbaBytes), rgbaStride, y_plane, width, u_plane, width / 2, v_plane, width / 2, width, height ); env->ReleaseByteArrayElements(rgbaData, rgbaBytes, 0); env->ReleaseByteArrayElements(i420Data, i420Bytes, result); return result; } 三、跨设备兼容性处理 不同设备的 RGBA 字节序可能不同,为确保兼容性,可通过以下方式动态选择转换函数: 1. 添加字节序检测逻辑 cpp // 检测 RGBA 字节序(在初始化时调用一次) bool isABGRLayout() { // 创建一个已知的红色像素 (R=255, G=0, B=0, A=255) uint32_t redPixel = 0xFF0000FF; // 假设为 ABGR 布局 uint8_t* bytes = reinterpret_cast(&redPixel); // 检查第1个字节是 A (0xFF) 还是 R (0xFF) if (bytes[0] == 0xFF && bytes[1] == 0x00) { return true; // ABGR 布局 } else { return false; // RGBA 布局 } } 2. 根据检测结果选择函数 cpp // 在转换时根据检测结果选择函数 if (isABGRLayout()) { libyuv::ABGRToI420(...); } else { libyuv::RGBAToI420(...); } /** * 将RGBA数据转换为YUV420SP(NV21)格式 * * 2. YUV420 * * YUV420p: I420、YV12 * YUV420sp: NV12、NV21 * 同样,对于一个6*4的图像,这四种像素格式的存储方式如下: * * Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y * Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y * Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y * Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y * U U U U U U V V V V V V U V U V U V V U V U V U * V V V V V V U U U U U U U V U V U V V U V U V U * - I420 - - YV12 - - NV12 - - NV21 - * 注: * * I420、YV12三个分量均为平面格式,即分别存在三个Byte型数组中; * NV12、NV21的存储格式为Y平面,UV打包,即Y信息存储在一个数组中,UV信息存储在一个矩阵中。 * * @param rgba 输入的RGBA数据,每个像素占4字节(ARGB) * @param width 图像宽度 * @param height 图像高度 * @return 转换后的YUV420SP数据 */