【问题标题】:Fast transformation of BGR BufferedImage to YUV using FFMpeg使用 FFMpeg 将 BGR BufferedImage 快速转换为 YUV
【发布时间】:2016-12-04 17:54:44
【问题描述】:

我想通过 JNI 使用 FFMpeg 的 sws_scale 函数将 Java 中的 TYPE_3BYTE_BGR BufferedImage 转换为 yuv。我首先从 BufferedImage 中提取我的图像数据为

    byte[] imgData = ((DataBufferByte) myImage.getRaster().getDataBuffer()).getData();

    byte[] output = processImage(toSend,0);     

然后我将它传递给作为本机函数的 processImage 函数。 C++ 端是这样的:

JNIEXPORT jbyteArray JNICALL Java_jni_JniExample_processData
  (JNIEnv *env, jobject obj, jbyteArray data, jint index)
{

    jboolean isCopy;
    uint8_t *test  = (uint8_t *)env->GetPrimitiveArrayCritical(data, &isCopy);
    uint8_t *inData[1]; // RGB24 have one plane
    inData[0] = test;


SwsContext * ctx = sws_getContext(width,height,AV_PIX_FMT_BGR24, (int)width, (int)width,
        AV_PIX_FMT_YUV420P, 0, 0, 0, 0);

    int lumaPlaneSize = width *height;
    uint8_t *yuv[3];

    yuv[0] = new uint8_t[lumaPlaneSize];
    yuv[1] = new uint8_t[lumaPlaneSize/4];
    yuv[2] = new uint8_t[lumaPlaneSize/4];

    int inLinesize[1] = { 3*nvEncoder->width }; // RGB stride
    int outLinesize[3] = { 3*width ,3*width ,3*width }; // YUV stride

    sws_scale(ctx, inData, inLinesize, 0, height , yuv, outLinesize);

但是,在运行代码后,我收到警告:[swscaler @ 0x7fb598659480] Warning: data is not aligned! This can lead to a speedloss, everything crashes.,最后一行所有内容都崩溃了。在将正确的参数传递给 sws_scale 方面,我是否正确地做事? (特别是步伐)。

更新: 这里有一个单独的错误:SwsContext * ctx = sws_getContext(width,height,AV_PIX_FMT_BGR24, (int)width, (int)width,0,NULL,NULL,NULL) 应该改为:SwsContext * ctx = sws_getContext(width,height,AV_PIX_FMT_BGR24, (int)height, (int)width,0,NULL,NULL,NULL)

【问题讨论】:

    标签: java c++ ffmpeg java-native-interface


    【解决方案1】:

    我看到的第一个问题 - 输出图像的步幅错误:

    yuv[0] = new uint8_t[lumaPlaneSize];
    yuv[1] = new uint8_t[lumaPlaneSize/4];
    yuv[2] = new uint8_t[lumaPlaneSize/4];
    
    int inLinesize[1] = { 3*nvEncoder->width }; // RGB stride
    int outLinesize[3] = { 3*width ,3*width ,3*width }; // YUV stride
    //                     ^^^^^^^  ^^^^^^^  ^^^^^^^
    

    分配的平面不够大,无法跨步。 YUV420 每个通道使用一个字节,所以3 是多余的,会导致边界违规。当转到下一行时,由于重新缩放器会跳过很多空间。接下来,实际的色度宽度是亮度宽度的一半,所以如果你想要在线条末端没有间隙的紧密排列的亮度和色度平面,请使用 next:

    int outLinesize[3] = { width , width / 2 , width / 2 }; // YUV stride
    

    分配大小保持不变。

    【讨论】:

    • 感谢您的回复,但我仍有一些问题。如果我将步幅调整为 yuv[1] = new uint8_t[lumaPlaneSize/2];和 yuv[1] = new uint8_t[lumaPlaneSize/2]; ,您建议的 outputLinesize 似乎一切正常。除非我将输出写入文件: fwrite(yuv[0], 1, lumaPlaneSize, fp); fwrite(yuv[1], 1, lumaPlaneSize/4, fp); fwrite(yuv[2], 1, lumaPlaneSize/4, fp);似乎图像已缩放且不完整。我改为 fwrite(yuv[1 and 2], 1, lumaPlaneSize/2, fp);那么图像完全不好我该怎么办?
    • 不完整的图像到底是什么样的?
    • 我猜sws_getContext() 的第五个参数必须是高度,而不是宽度。
    • 真是罪孽……非常感谢!
    【解决方案2】:

    查看source,特别是在第 321 行附近,如果您的系统支持 AVX2 指令并且各种指针和大小不是 16 的倍数,您会收到该警告消息。崩溃可能是因为您传入的数组、inDatainLineSizeoutLinesize 的大小不合适。指针数组至少需要 3 个元素,而跨步数组需要 4 个元素。在 sws_scale 的某个位置,它正在访问数组边界之外的 inData[1],从而导致指针错误。

    【讨论】:

    • "指针数组至少需要 3 个元素,而步幅数组需要 4 个 ..." - 不,这些大小由使用的像素格式严格定义,在 YUV420 的情况下,两个缓冲区数组并且 stride 数组必须恰好包含 3 个元素 - 对于 Y、Cr、Cb 平面各有一个。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多