devolay icon indicating copy to clipboard operation
devolay copied to clipboard

Native crash when calling DevolayReceiver.close()

Open vatbub opened this issue 1 year ago • 0 comments

Hi there :) I am using the following code to capture frames using NDI and this library:

class NDIIssueSampleCode {
    var isConnected: Boolean = false
        private set

    var sourceName: String = "myNdiSource"
    var ndiGroup = "myNdiGroup"
    var ip = InetAddress.getLocalHost()

    private val finder = DevolayFinder(true, ndiGroup, ip.hostAddress)
    private var devolayReceiver: DevolayReceiver? = null
    private var captureThread: ThreadWithCancelFlag? = null

    fun connect(onNextFrameReceived: (frame: Mat) -> Unit) {
        if (isConnected) return
        logger.debug("Connecting to NDI source '${sourceName}'...")

        logger.debug("Searching source in available sources...")
        finder.waitForSources(0)

        var source: DevolaySource? = null
        val endTime = LocalDateTime.now().plusSeconds(30)
        while (source == null && LocalDateTime.now().isBefore(endTime)) {
            source = finder.currentSources.firstOrNull { it.sourceName == sourceName }
        }
        if (source == null) {
            disconnect()
            throw NoSuchElementException("Source '${sourceName}' not found on the network.")
        }

        logger.debug("Source found, connecting...")
        val receiver = DevolayReceiver(DevolayReceiver.ColorFormat.BGRX_BGRA, DevolayReceiver.RECEIVE_BANDWIDTH_HIGHEST, false, null)
        receiver.connect(source)
        this.devolayReceiver = receiver

        logger.debug("Starting NDI capture thread...")
        captureThread = createAndStartConnectThread(receiver, onNextFrameReceived)
    }

    private fun createAndStartConnectThread(receiver: DevolayReceiver, onNextFrameReceived: (frame: Mat) -> Unit) =
        ThreadWithCancelFlag {
            Thread {
                manageResources {
                    val videoFrame = DevolayVideoFrame().closeLater()
                    val audioFrame = DevolayAudioFrame().closeLater()
                    val metadataFrame = DevolayMetadataFrame().closeLater()

                    while (!cancelled) {
                        val captureResult = receiver.receiveCapture(
                            videoFrame,
                            audioFrame,
                            metadataFrame,
                            1000
                        )
                        if (captureResult != DevolayFrameType.VIDEO) continue

                        val openCvFrame = videoFrame.convertToOpenCvFrame(DevolayFrameFourCCType.BGRA)
                        onNextFrameReceived(openCvFrame)
                    }

                    logger.debug("NDI capture thread exiting now...")
                }
            }
        }.also {
            it.thread.name = "ndi-capture-thread"
            it.thread.start()
        }

    fun disconnect() {
        logger.debug("Disconnecting from NDI capture...")
        isConnected = false
        captureThread?.let {
            it.cancelled = true
            it.thread.join()
        }
        captureThread = null
        devolayReceiver?.close()
        devolayReceiver = null
    }
}

However, when calling devolayReceiver?.close(), the JVM crashes in native code. In summary, the NDI Sdk crashes with the following stacktrace:

Stack: [0x0000006471600000,0x0000006471700000],  sp=0x00000064716fce70,  free space=1011k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
C  [ntdll.dll+0xabf81]
C  [ntdll.dll+0x3ab11]
C  [msvcrt.dll+0x1cadc]
C  0x000001b720ea3378

Java frames: (J=compiled Java code, j=interpreted, Vv=VM code)
j  me.walkerknapp.devolay.DevolayReceiver.receiveDestroy(J)V+0
j  me.walkerknapp.devolay.DevolayReceiver.close()V+4
j  com.github.vatbub.myProject.capture.NDIInput.disconnect()V+71
// Stack trace shortened for NDA and brevity reasons...
j  java.lang.Thread.run()V+13 [email protected]
v  ~StubRoutines::call_stub 0x000001b720e8100e

So, the crash happens somewhere in native code when trying to disconnect from the DevolayReceiver. I am still debugging this, but here is the information that I found so far:

  • There is no pending (or in progress) call to receiver.receiveCapture() as the cancelled flag of captureThread is set first and the disconnect method waits for captureThread to exit (using it.thread.join()) before calling receiver.close().
  • The crash is reproducible.

One further hypothesis, which I cannot confirm right now, is that removing the call to receiver.close() avoids the crash, but it also seems to leave the connection to the NDI source open even though no further calls to receiver.receiveCapture() are made. The reason for this hypothesis is that when running the software in the field with a real NDI camera, I was observing a higher network bandwidth than I expected even though I was no longer calling receiver.receiveCapture(). However, since I don't have this camera available to me right now, I can only use NDI Tools to create simulated NDI sources which don't appear as network bandwidth in Task Manager.

If you are still actively developing this library, I would be very grateful if you could help me with this issue, as it is very difficult to find out what's going on there in native code.

Other than that, I really love this library and it made integrating NDI into my project so easy, so thank you very much and Cheers :)

The full crash log and crash dump are as follows:

#
# A fatal error has been detected by the Java Runtime Environment:
#
#  EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x00007ffaacc7bf81, pid=16868, tid=6048
#
# JRE version: OpenJDK Runtime Environment Temurin-19.0.2+7 (19.0.2+7) (build 19.0.2+7)
# Java VM: OpenJDK 64-Bit Server VM Temurin-19.0.2+7 (19.0.2+7, mixed mode, sharing, tiered, compressed oops, compressed class ptrs, g1 gc, windows-amd64)
# Problematic frame:
# C  [ntdll.dll+0xabf81]
#
# No core dump will be written. Minidumps are not enabled by default on client versions of Windows
#
# An error report file with more information is saved as:
# D:\git\myProject\hs_err_pid16868.log
#
# If you would like to submit a bug report, please visit:
#   https://github.com/adoptium/adoptium-support/issues
# The crash happened outside the Java Virtual Machine in native code.
# See problematic frame for where to report the bug.
#

crashDump.txt

vatbub avatar Mar 17 '24 13:03 vatbub