简体   繁体   English

FFMPEG和JNI - 将AVFrame数据传递给Java和Back

[英]FFMPEG and JNI - pass AVFrame data to Java and Back

I have some C code that decodes a video frame by frame. 我有一些C代码逐帧解码视频。 I get to a point where i have an AVFrame in BGR32 and would like to send it back to Java for editing. 我到了一个点,我在BGR32中有一个AVFrame,并希望将它发送回Java进行编辑。

I have a ByteBuffer object in my C code that was created in Java using AllocateDirect but i struggle to write the content of the AVFrame->data[0] (of uint8_t type) to it and read it back. 我的C代码中有一个ByteBuffer对象是使用AllocateDirect在Java中创建的,但我很难将AVFrame-> data [0](uint8_t类型)的内容写入其中并将其读回。 I have tried memcpy with no luck. 我试过memcpy而没有运气。 Does anyone have an idea how to achieve this? 有谁知道如何实现这一目标?

UPDATE Followed Will's comment below and wrote this in C 更新跟随Will的评论如下,并在C中写道

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

The buffer does contain some data in Java but doing the following returns a null bitmap 缓冲区确实包含Java中的一些数据,但执行以下操作会返回空位图

BufferedImage frame = ImageIO.read(bitmapStream);

Where bitmapStream is a ByteBufferInputStream defined here: https://code.google.com/p/kryo/source/browse/trunk/src/com/esotericsoftware/kryo/io/ByteBufferInputStream.java?r=205 其中bitmapStream是这里定义的ByteBufferInputStream: https//code.google.com/p/kryo/source/browse/trunk/src/com/esotericsoftware/kryo/io/ByteBufferInputStream.java? r = 205

Not sure if I am not writing things correctly in this buffer 不确定我是不是在这个缓冲区中正确写东西

UPDATE 2 更新2

Got pretty close now thanks to the latest snippet. 由于最新的片段,现在已经非常接近了。 I am using BGR32 format in my C code ie 4 bytes per pixel. 我在我的C代码中使用BGR32格式,即每像素4个字节。 So I modified things a bit in Java: 所以我在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);

I get the image correctly but there seems to be an issue with color channels 我正确地获得了图像,但颜色通道似乎存在问题 例

Tried different formats with no luck 尝试了不同的格式,没有运气

From Oracle's JNI Functions Documentation at 来自Oracle的JNI Functions文档
https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#GetDirectBufferAddress https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#GetDirectBufferAddress

GetDirectBufferAddress GetDirectBufferAddress

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

Fetches and returns the starting address of the memory region referenced by the given direct java.nio.Buffer. 获取并返回给定直接java.nio.Buffer引用的内存区域的起始地址。

This function allows native code to access the same memory region that is accessible to Java code via the buffer object. 此函数允许本机代码通过缓冲区对象访问Java代码可访问的相同内存区域。 LINKAGE: 连锁:

Index 230 in the JNIEnv interface function table. JNIEnv接口函数表中的索引230。 PARAMETERS: 参数:

env: the JNIEnv interface pointer env:JNIEnv接口指针

buf: a direct java.nio.Buffer object (must not be NULL) RETURNS: buf:直接java.nio.Buffer对象(不能为NULL)RETURNS:

Returns the starting address of the memory region referenced by the buffer. 返回缓冲区引用的内存区域的起始地址。 Returns NULL if the memory region is undefined, if the given object is not a direct java.nio.Buffer, or if JNI access to direct buffers is not supported by this virtual machine. 如果内存区域未定义,如果给定对象不是直接java.nio.Buffer,或者此虚拟机不支持对直接缓冲区的JNI访问,则返回NULL。 SINCE: 以来:

JDK/JRE 1.4 JDK / JRE 1.4

I tested with this C++ code: 我测试了这个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);
}

and this Java Code: 和这个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);
}

And it printed the first_byte=77 indicating it got the data copied correctly. 它打印first_byte = 77表示它正确复制了数据。

Update 更新

ImageIO.read() will not accept just any set of bytes it has to be in a format that one of the installed ImageReader's can recognize such as JPEG or PNG. ImageIO.read()不会接受任何字节集,它必须采用安装的ImageReader之一可识别的格式,如JPEG或PNG。

Here is an example getting raw for (3 byte r,g,b )bytes into an image 下面是将(3字节r,g,b)字节生成为图像的示例

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);

Update 2 更新2

For BGR32 I believe this would be closer: 对于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);

Notice where I have commented, where I suspect you may need to experiment with the array of bandOffsets in the third argument of the ComponentSampleModel constructor to fix the color model. 注意我评论过的地方,我怀疑你可能需要在ComponentSampleModel构造函数的第三个参数中试验bandOffsets数组来修复颜色模型。

Update 3 更新3

One can reuse the sampleModel to get data out of the image by using BufferedImage.copyData() to a WritableRaster instead of using getRaster(). 通过使用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);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM