JNI 和另一个线程的 ArrayStoreException 错误

ArrayStoreException error with JNI and another thread

我试图通过使用 JNI 直接从视频文件中读取帧并以本机代码解码帧,将它们作为原始 3 字节 BGR 数组传回。我还使用 jvmti 的 SetTagGetTag 到 "tag" Java 对象,通过指向 long long.[=22= 的指针对应 struct ]

我只是在 JNI/JVMTI 和线程方面遇到了一个小问题。如果在调用我的本机代码 (read()) 之间启动线程,对 Java 的回调会导致数组的长度为 0,而在本机端,数组仍保持其正常大小.我一直假设它与 JNIEnv* 有关,因为 JNIEnv 是特定于线程的,但是,我看不到启动空线程如何影响这一点,也不能我找到了支持我假设的证据。

以下代码导致了问题:

package ga.nurupeaches.imgmap.natives;

import ga.nurupeaches.imgmap.utils.YTRegexHelper;
import java.io.IOException;

public class NativeVideoTest {

    public static final int WIDTH = 0, HEIGHT = 0;
    public static final String videoPath = YTRegexHelper.getDirectLinks("rnQBF2CIygg").get(0);
    private NativeVideo video;

    public static void main(String[] args) throws Exception {
        System.load("/home/tsunko/Gunvarrel/ImgMap-rw/src/main/cplusplus/libNativeVideo.so");
        NativeVideo.initialize(DebugCallbackHandler.class);

        NativeVideoTest test = new NativeVideoTest();
        test.nativeWork();
    }

    public NativeVideoTest() throws IOException {
        video = new NativeVideo(new DebugCallbackHandler(this, 1280, 720), WIDTH, HEIGHT);
    }

    public void nativeWork() throws InterruptedException {
        try{
            video.open(videoPath);
        } catch (Exception e) {
            e.printStackTrace();
        }

        video.read();
        new Thread().start();
        video.read(); // Returns 0 length array.
    }

}

read() 实现为:

JNIEXPORT void JNICALL Java_ga_nurupeaches_imgmap_natives_NativeVideo_read(JNIEnv* env, jobject jthis, jobject callback){
    NativeVideoContext* context = getContext(env, jthis, true);
    if(context == NULL){
        return;
    }

    std::cout << "read: reading frame" << std::endl;
    while(av_read_frame(context->formatContext, &(context->packet)) >= 0){
        std::cout << "read: recv packet" << std::endl;
        if(context->packet.stream_index == context->videoStreamId){
            std::cout << "read: recv video packet" << std::endl;
            avcodec_decode_video2(context->codecContext, context->rawFrame, &(context->frameFinished), &(context->packet));
            std::cout << "read: decoded video" << std::endl;

            if(context->frameFinished){
                std::cout << "read: finished frame; scaling" << std::endl;
                sws_scale(context->imgConvertContext, (const uint8_t* const*)context->rawFrame->data,
                            context->rawFrame->linesize, 0, context->codecContext->height,
                            context->rgbFrame->data, context->rgbFrame->linesize);
                std::cout << "read: scaled image; freeing packet and breaking loop" << std::endl;

                av_free_packet(&(context->packet));
                break;
            }
        }

        av_free_packet(&(context->packet));
    }

    std::cout << "read: init final returning" << std::endl;
    std::cout << "read: beforeSetByteArrayRegion javaArray@" << &(context->javaArray) << ";typeid=" << typeid(context->javaArray).name() << ";bufferSize=" << context->bufferSize << std::endl;
    env->SetByteArrayRegion(context->javaArray, 0, context->bufferSize, (jbyte*)(context->rgbFrame->data[0]));
    doCallback(env, callback, context->javaArray);
    std::cout << "read: afterSetByteArrayRegion javaArray@" << &(context->javaArray) << ";typeid=" << typeid(context->javaArray).name() << ";bufferSize=" << context->bufferSize << std::endl;
}

日志:

_initialize: found id
2764800
entry@getTag: checking jvmti
tag@getTag: null
entry@setTag: checking jvmti
tag@setTag: 140074857163376
entry _open: grabbing context
entry@getTag: checking jvmti
tag@getTag: 140074857163376
_open: nullcheck context
_open: setting source and formatContext
_open: opening input
_open: finding stream info
_open: finding video stream id
_open: fixing any 0 width/height
_open: finding decoder for codec
_open dbg: codecContext@0x7f65b81a6b20
_open dbg: codec_id=28
_open: opening decoder
_open: alloc frames
_open: nullcheck frames
_open: init swscale context
_open: init buffers
_open: reserving 2764800 bytes of memory for our buffer and etc.
entry@getTag: checking jvmti
tag@getTag: 140074857163376
read: reading frame
read: recv packet
read: recv video packet
read: decoded video
read: recv packet
read: recv video packet
read: decoded video
read: recv packet
read: recv video packet
read: decoded video
read: recv packet
read: recv video packet
read: decoded video
read: recv packet
read: recv video packet
read: decoded video
read: finished frame; scaling
read: scaled image; freeing packet and breaking loop
read: init final returning
read: beforeSetByteArrayRegion javaArray@0x7f65b81a6b10;typeid=P11_jbyteArray;bufferSize=2764800
read: afterSetByteArrayRegion javaArray@0x7f65b81a6b10;typeid=P11_jbyteArray;bufferSize=2764800
entry@getTag: checking jvmti
tag@getTag: 140074857163376
read: reading frame
read: recv packet
read: recv video packet
read: decoded video
read: finished frame; scaling
read: scaled image; freeing packet and breaking loop
read: init final returning
read: beforeSetByteArrayRegion javaArray@0x7f65b81a6b10;typeid=P11_jbyteArray;bufferSize=2764800
data.length=0;rawImage.length=2764800
read: afterSetByteArrayRegion javaArray@0x7f65b81a6b10;typeid=P11_jbyteArray;bufferSize=2764800
Exception in thread "main" java.lang.ArrayStoreException
    at java.lang.System.arraycopy(Native Method)
    at ga.nurupeaches.imgmap.natives.DebugCallbackHandler.handleData(DebugCallbackHandler.java:30)
    at ga.nurupeaches.imgmap.natives.NativeVideo.read(Native Method)
    at ga.nurupeaches.imgmap.natives.NativeVideo.read(NativeVideo.java:31)
    at ga.nurupeaches.imgmap.natives.NativeVideoTest.nativeWork(NativeVideoTest.java:33)
    at ga.nurupeaches.imgmap.natives.NativeVideoTest.main(NativeVideoTest.java:18)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)

Process finished with exit code 1

可在 https://github.com/CirnoTheGenius/ImgMap-rw/tree/master/src/main/cplusplus

获取其他任何内容的相关代码

请注意,请原谅我在 C++ 中犯下的任何错误;我对它还是比较陌生。

我没有通过 NewByteArray 使用 Java 字节数组,而是直接传递 ByteBuffer 并获取 ByteBuffer 的内存区域地址并使用 memcpycontext->rgbFrame->data[0] 复制到其中,然后在不带任何参数的情况下调用 doCallback() 以通知回调处理程序已完成。