【问题标题】:FFMPEG and JNI - pass AVFrame data to Java and BackFFMPEG 和 JNI - 将 AVFrame 数据传递给 Java 并返回
【发布时间】:2016-01-03 02:22:39
【问题描述】:

我有一些 C 代码逐帧解码视频。我到了在 BGR32 中有一个 AVFrame 并想将其发送回 Java 进行编辑的地步。

我的 C 代码中有一个 ByteBuffer 对象,该对象是使用 AllocateDirect 在 Java 中创建的,但我很难将 AVFrame->data[0](uint8_t 类型)的内容写入它并且读回来。我试过 memcpy 没有运气。有谁知道如何实现这一目标?

更新 跟随威尔在下面的评论并用C写了这个

char *buf = (*pEnv)->GetDirectBufferAddress(pEnv, byteBuffer);
memcpy(buf, rgb_frame->data[0], output_width*output_height*4);

缓冲区确实包含 Java 中的一些数据,但执行以下操作会返回空位图

BufferedImage frame = ImageIO.read(bitmapStream);

其中 bitmapStream 是此处定义的 ByteBufferInputStream: https://code.google.com/p/kryo/source/browse/trunk/src/com/esotericsoftware/kryo/io/ByteBufferInputStream.java?r=205

不确定我是否在此缓冲区中没有正确写入内容

更新 2

感谢最新的 sn-p,现在已经非常接近了。我在我的 C 代码中使用 BGR32 格式,即每像素 4 个字节。所以我在 Java 中做了一些修改:

final byte[] dataArray = new byte[width*height*4];
imageData.get(dataArray);
final BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_BGR);
final DataBuffer buffer = new DataBufferByte(dataArray, dataArray.length);
Raster raster = Raster.createRaster(sampleModel, buffer, null);
image.setData(raster);

我得到了正确的图像,但颜色通道似乎有问题

尝试了不同的格式,但没有成功

【问题讨论】:

  • 编辑答案以响应您的更新
  • 编辑答案以再次响应您的更新
  • 我会推荐尝试 TYPE_4BYTE_ABGR
  • 我根本无法让 4BYTE_BGR 工作来拍照。最终起作用的正确代码是下面建议的代码。
  • 另一种选择是在下面重新编辑的答案中使用 image.copyData() 示例

标签: java c ffmpeg java-native-interface


【解决方案1】:

来自 Oracle 的 JNI 函数文档
https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#GetDirectBufferAddress

获取直接缓冲区地址

void* GetDirectBufferAddress(JNIEnv* env, jobject buf);

获取并返回内存区域的起始地址 由给定的直接 java.nio.Buffer 引用。

这个函数允许本地代码访问相同的内存区域 Java 代码可以通过缓冲区对象访问。链接:

JNIEnv 接口函数表中的索引 230。参数:

env:JNIEnv 接口指针

buf:一个直接的 java.nio.Buffer 对象(不能为 NULL) RETURNS:

返回被引用的内存区域的起始地址 缓冲。如果内存区域未定义,则返回 NULL,如果给定 对象不是直接java.nio.Buffer,或者如果JNI直接访问 此虚拟机不支持缓冲区。自:

JDK/JRE 1.4

我用这个 C++ 代码进行了测试:

static char framebuf[100];

JNIEXPORT void JNICALL Java_javaapplication45_UseByteBuffer_readBuf
  (JNIEnv *env, jobject usebb, jobject bb) {
    void *addr = env->GetDirectBufferAddress(bb);
    framebuf[0] = 77;
    memcpy(addr,framebuf,100);
}

还有这个 Java 代码:

public class UseByteBuffer {
    public native void readBuf(ByteBuffer bb);
}

...

public static void main(String[] args) {
    System.load("/home/shackle/NetBeansProjects/usebb/dist/Debug/GNU-Linux-x86/libusebb.so");
    ByteBuffer bb = ByteBuffer.allocateDirect(100);
    new UseByteBuffer().readBuf(bb);
    byte first_byte = bb.get(0);
    System.out.println("first_byte = " + first_byte);
}

它打印了 first_byte=77,表明它正确复制了数据。

更新

ImageIO.read() 不会只接受任何字节集,它必须采用已安装的 ImageReader 可以识别的格式,例如 JPEG 或 PNG。

这是一个将 (3 byte r,g,b )bytes 的原始图像转换为图像的示例

int width = 256;
int height = 256;
ByteBuffer bb = ByteBuffer.allocateDirect(height*width*3);

byte[] raw = new byte[width * height * 3];
bb.get(raw);
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
DataBuffer buffer = new DataBufferByte(raw, raw.length);
SampleModel sampleModel = new ComponentSampleModel(DataBuffer.TYPE_BYTE, width, height, 3, width * 3, new int[]{0,1,2});
Raster raster = Raster.createRaster(sampleModel, buffer, null);
image.setData(raster);

更新 2

对于 BGR32,我相信这会更接近:

ByteBuffer imageData = ByteBuffer.allocateDirect(height * width * 4);
byte[] raw = new byte[width * height * 4];
imageData.get(raw);
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_BGR);
DataBuffer buffer = new DataBufferByte(raw, raw.length);
SampleModel sampleModel = new ComponentSampleModel(
        DataBuffer.TYPE_BYTE, width, height, 4, width * 4,
        new int[]{2,1,0} // Try {1,2,3}, {3,2,1}, {0,1,2}
);
Raster raster = Raster.createRaster(sampleModel, buffer, null);
image.setData(raster);

请注意我评论的地方,我怀疑您可能需要在 ComponentSampleModel 构造函数的第三个参数中试验 bandOffsets 数组来修复颜色模型。

更新 3

可以通过使用 BufferedImage.copyData() 到 WritableRaster 而不是使用 getRaster() 来重用 sampleModel 从图像中获取数据。

SampleModel sampleModel = new ComponentSampleModel(
        DataBuffer.TYPE_BYTE, width, height, 4, width * 4,
        new int[]{2, 1, 0} 
);

...

BufferedImage newImage = ImageIO.read(new File("test.png"));
byte newRaw[] = new byte[height*width*4];
DataBuffer newBuffer = new DataBufferByte(newRaw, newRaw.length);
WritableRaster newRaster = Raster.createWritableRaster(sampleModel, newBuffer, null);
newImage.copyData(newRaster);

【讨论】:

  • 这成功了!我相信我的色彩空间仍然存在问题
  • 您的原始答案是正确的 ( {0,1,2} )。我没有尝试过认为我需要 4 个数字,所以对上面的图像尝试了 {0,1,2,3} 以及所有具有 4 个数字的变体,但最后在网上看到 BGR32 只有 RGB 分量,最后一个8 个字节未使用...非常感谢您的帮助!
猜你喜欢
  • 1970-01-01
  • 2015-09-27
  • 1970-01-01
  • 2018-02-17
  • 1970-01-01
  • 2016-04-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多