Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Native crash when calling DevolayReceiver.close() #32

Closed
vatbub opened this issue Mar 17, 2024 · 1 comment
Closed

Native crash when calling DevolayReceiver.close() #32

vatbub opened this issue Mar 17, 2024 · 1 comment

Comments

@vatbub
Copy link

vatbub commented Mar 17, 2024

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

@WalkerKnapp
Copy link
Owner

WalkerKnapp commented Jul 14, 2024

Thank you for your report! This should be fixed by #33, thanks to @kmod-midori! I will soon put out the 2.1.1 release with this change

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants