Search code examples
androidc++java-native-interface

I am a novice JNI,Why doesn't my android jni C ++ try block catch an exception


I am a novice JNI,Why doesn't my android jni C ++ try block catch an exception,The code crashes when and the app crashes without jumping to exception handling this is my code

Activity re code Receive H264 data with queue at JAVA layer Start a decoding thread and continuously take a frame of H264 data packets from the queue into the C ++ layer to decode with FFmpeg

    class decode extends Thread {
        @Override
        public void run() {
//            super.run();
            while (isDecode) {
                byte[] data = one.poll();
                if (data != null)
                    if (ffmpegUtilsInSignalOne != null)
                        ffmpegUtilsInSignalOne.decodeH264One(data);
            }
        }
    }

cpp File code

extern "C"
JNIEXPORT void JNICALL
Java_cn_zhihuiyun_control_utils_FFMPEGUtils_decodeH264One(JNIEnv *env, jobject thiz,
                                                          jbyteArray data) {
    if (packetOne != nullptr && pCodecCtxOne != nullptr && vFrameOne != nullptr) {
        if (data == nullptr)
            return;;
        if (isPlay == 0)
            return;
        jbyte *arr = env->GetByteArrayElements(data, JNI_FALSE);
        packetOne->data = (uint8_t *) arr;
        packetOne->size = env->GetArrayLength(data);
        avcodec_send_packet(pCodecCtxOne, packetOne);
        avcodec_receive_frame(pCodecCtxOne, vFrameOne);
        av_packet_unref(packetOne);
        env->ReleaseByteArrayElements(data, arr, 0);
    }
}

At this time, a display thread is started in the C layer for screen rendering

extern "C"
JNIEXPORT void JNICALL
Java_cn_zhihuiyun_control_utils_FFMPEGUtils_initVisThread(JNIEnv *env, jobject thiz) {
    isPlay = 1;
    threadVister = pthread_create(&threadVister, nullptr,
                                  reinterpret_cast<void *(*)(void *)>(&showVideo),
                                  (void *) env);
    pthread_detach(threadVister);
}

void *showVideo(JNIEnv *env) {
    try {
        while (isPlay == 1) {
            if (vFrameOne != nullptr && pFrameRGBAOne != nullptr && pCodecCtxOne != nullptr &&
                nativeWindowOne != nullptr) {
                ANativeWindow_lock(nativeWindowOne, &windowBufferOne, nullptr);
                av_image_fill_arrays(pFrameRGBAOne->data, pFrameRGBAOne->linesize,
                                     (const uint8_t *) windowBufferOne.bits, AV_PIX_FMT_RGBA,
                                     detWidth, detHeight, 1);
                int re = -1;
                try {
                    re = libyuv::I420ToARGB(vFrameOne->data[0], vFrameOne->linesize[0],
                                            vFrameOne->data[2], vFrameOne->linesize[2],
                                            vFrameOne->data[1], vFrameOne->linesize[1],
                                            pFrameRGBAOne->data[0], pFrameRGBAOne->linesize[0],
                                            pCodecCtxOne->width, pCodecCtxOne->height);
                    if (env->ExceptionCheck()) {
                        env->ExceptionDescribe();
                        env->ExceptionClear();
                    }
                } catch (...) {

                }
                if (re == -1) {

                } else {
                    ANativeWindow_unlockAndPost(nativeWindowOne);

                }
            }
            }
        }
    } catch (...) {
        printf("error");

    }
    if (vFrameOne != nullptr) {
        av_frame_free(&vFrameOne);
        vFrameOne = nullptr;
    }
    if (packetOne != nullptr) {
        av_free(packetOne);
        packetOne = nullptr;
    }
    if (pFrameRGBAOne != nullptr) {
        av_free(pFrameRGBAOne);
        pFrameRGBAOne = nullptr;
    }
    if (pCodecCtxOne != nullptr) {
        avcodec_free_context(&pCodecCtxOne);
        pCodecCtxOne = nullptr;
    }
    if (nativeWindowOne != nullptr) {
        ANativeWindow_release(nativeWindowOne);
        nativeWindowOne = nullptr;
    }

    pthread_exit(&threadVister);
}

Solution

  • You should carefully read the documentation of avcodec_receive_frame:

    Return decoded output data from a decoder.
    Parameters
    avctx codec context
    frame This will be set to a reference-counted video or audio frame (depending on the decoder type) allocated by the decoder. Note that the function will always call av_frame_unref(frame) before doing anything else.

    Returns 0: success, a frame was returned
    AVERROR(EAGAIN): output is not available in this state - user must try to send new input
    AVERROR_EOF: the decoder has been fully flushed, and there will be no more output frames AVERROR(EINVAL): codec not opened, or it is an encoder other negative values: legitimate decoding errors

    I have highlighted two key pieces of information:

    • First, the call to avcodec_receive_frame will invalidate vFrameOne. If your other thread is in the middle of decoding, your program will crash. You will need to establish a synchronization mechanism between receiving and displaying threads to make sure the receiver is always receiving into a frame only it has access to, and that it only passes full frames to the displaying side. (see next point)
    • Second, you should check the return value of avcodec_receive_frame. If you see an AVERROR(EAGAIN) you have not received enough packets to produce a full frame. Only if this function produces 0 can you take the full frame and hand it over to the displaying thread.