基于FFMpeg的BGR缓冲图像到YUV的快速转换

Fast transformation of BGR BufferedImage to YUV using FFMpeg

本文关键字:YUV 转换 图像 FFMpeg BGR 缓冲 基于      更新时间:2023-10-16

我想通过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)

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

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是冗余的,并导致绑定冲突。由于重新缩放程序在转到下一行时会跳过大量空间。接下来,实际的色度宽度是亮度宽度的一半,所以如果你想要紧凑的亮度和色度平面,在行末端没有间隙,请使用下一个:

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

分配大小保持不变。

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