Closed chammond75 closed 6 years ago
There is most likely some native dependency missing. There is a cause to that exception, maybe it's not showing up for some reason. Alpine is known to be quite buggy though, so it might take a while to figure out the issue, and even when we do, it probably won't be easy to fix. In any case, please try to load the modules manually and see if we can get any better stack traces:
Loader.load(avutil.class);
Loader.load(swresample.class);
Loader.load(avcodec.class);
ok, let me try these commands and see what I can capture. Thank you.
EDIT: I'm assuming these are the classes in org.bytedeco.javacpp and NOT the ones in org.bytedeco.javacpp.presets. Is that correct?
Also @saudet is there a recommended version of Linux to use? It may be more feasible for me to switch. In the meantime I'll try to gather the info you requested.
Yes, not the ones in the presets package.
As mentioned here, the binaries are built with CentOS: https://github.com/bytedeco/javacpp-presets#manual-installation
Tried loading each of these 3 classes in their own try/catch block (so 3 try/catches in total). avutil and swresample loaded without problem. avcodec did not. Unfortunately there was no more information in the exception than what I've posted before. I'm going to try switching to Centos 7 and see if that can work for me.
@saudet switched over to Centos 7 and am STILL getting the same error. I'm at a loss. What can I do!?!?
ok, here's what I was able to find:
[root@9a3814890b6e javatmp]# ldd org/bytedeco/javacpp/linux-x86_64/libavcodec.so.57 ldd: warning: you do not have execution permission for `org/bytedeco/javacpp/linux-x86_64/libavcodec.so.57' linux-vdso.so.1 => (0x00007ffd9779e000) libswresample.so.2 => not found libavutil.so.55 => not found libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f13cc6ee000) libm.so.6 => /lib64/libm.so.6 (0x00007f13cc3ec000) libstdc++.so.6 => /lib64/libstdc++.so.6 (0x00007f13cc0e4000) libdl.so.2 => /lib64/libdl.so.2 (0x00007f13cbee0000) libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007f13cbcca000) libc.so.6 => /lib64/libc.so.6 (0x00007f13cb908000) /lib64/ld-linux-x86-64.so.2 (0x000055f532aea000)
The two missing shared objects are packaged in the jar, however they are not on the system's LD_LIBRARY_PATH. I notice that once I invoke the classes in javacpp a cache directory is created under ~/.javacpp which contains the exploded .so's. If I add that path to my LD_LIBRARY_PATH I see:
[root@c0b299fbec4a javatmp]# ldd /root/.javacpp/cache/ffmpeg-3.2.1-1.3-linux-x86_64.jar/org/bytedeco/javacpp/linux-x86_64/libavcodec.so.57 ldd: warning: you do not have execution permission for `/root/.javacpp/cache/ffmpeg-3.2.1-1.3-linux-x86_64.jar/org/bytedeco/javacpp/linux-x86_64/libavcodec.so.57' linux-vdso.so.1 => (0x00007ffd11f59000) libswresample.so.2 => /root/.javacpp/cache/ffmpeg-3.2.1-1.3-linux-x86_64.jar/org/bytedeco/javacpp/linux-x86_64/libswresample.so.2 (0x00007f8cf4f73000) libavutil.so.55 => /root/.javacpp/cache/ffmpeg-3.2.1-1.3-linux-x86_64.jar/org/bytedeco/javacpp/linux-x86_64/libavutil.so.55 (0x00007f8cf4cfc000) libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f8cf4adc000) libm.so.6 => /lib64/libm.so.6 (0x00007f8cf47da000) libstdc++.so.6 => /lib64/libstdc++.so.6 (0x00007f8cf44d2000) libdl.so.2 => /lib64/libdl.so.2 (0x00007f8cf42ce000) libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007f8cf40b8000) libc.so.6 => /lib64/libc.so.6 (0x00007f8cf3cf6000) librt.so.1 => /lib64/librt.so.1 (0x00007f8cf3aee000) /lib64/ld-linux-x86-64.so.2 (0x000055f539ec6000)
However just getting ldd working is not sufficient to getting my code to work. I think, in part, because I'm changing my LD_LIBRARY_PATH after the JVM starts. And also because that cache directory is created lazily. So now the challenge is: how do I proactively get these .so's into my LD_LIBRARY_PATH during construction of my Docker image?
Does it sound like I'm on the right track? Any suggestions?
Also I noticed this in the properties (this is when running on my Mac where I can step through and debug:
platform.cinclude=[<libavutil/avutil.h>, <libavutil/error.h>, <libavutil/mem.h>, <libavutil/mathematics.h>, <libavutil/rational.h>, <libavutil/log.h>, <libavutil/buffer.h>, <libavutil/pixfmt.h>, <libavutil/frame.h>, <libavutil/samplefmt.h>, <libavutil/channel_layout.h>, <libavutil/cpu.h>, <libavutil/dict.h>, <libavutil/opt.h>, <libavutil/pixdesc.h>, <libavutil/imgutils.h>, <libavutil/downmix_info.h>, <libavutil/stereo3d.h>, <libavutil/ffversion.h>, <libavutil/motion_vector.h>, <libavutil/fifo.h>, <libavutil/audio_fifo.h>, log_callback.h, <libswresample/swresample.h>, <libavcodec/avcodec.h>]}
However log_callback.h is missing on my Linux system. What is the impact of that missing header file?
EDIT: I should add that I copied all of the .so's packaged with the linux-x86_64 jar into my /usr/local/lib directory (so now ldd works) but that still has no resolved the issue. It feels like I just need to get these files into the right location for the library to load them. Any suggestions?
Also I added an extra log line to try to get the cause of the exception and it is blank
try {
Loader.load(avcodec.class);
} catch (final Throwable t) {
logger.error("Problem loading avcodec", t); <-- Prints NoClassDefFoundError
logger.error("Caused by", t.getCause()); <-- Prints nothing
}
Do I need to set on of the properties in the properties map to get this library to look in the right place for these shared objects (like "/lib64" instead of "/lib" or "/usr/local/lib")?
None of what you mention is a problem.
Maybe the binaries you have are corrupted. Have you tried to redownload them?
I could try to re-download. But can you explain something to me:
It looks like the .so's are loaded as resources from within the jar files (by the class loader) is that right? So then when the classes in the C/C++ layer are initialized where does it search to find the link dependencies? So if libavcodec.so.57 relies on libavutil.so.55 where does it find that dependency? Should it be finding it within the same jar (because that's where it's packaged). Or should it be finding it in ~/.javacpp/cache because that's where the get exploded? Or should it be finding it in $LD_LIBRARY_PATH because that's where libraries get resolved? Or should it be finding it in /lib or /lib64?
Ultimately I'm trying (desperately) to understand the JNI path here. Once Class.forName0() native method is invoked I have no insight into what's happening. So understanding that better, from your perspective, would be very helpful.
Also, what does it say that Loader.load(avutil.class) and Loader.load(swresample.class) work? Should I understand that to mean that libavutil.so.55 and libjniavutil.so as well as libswresample.so and libjniswresample.so are resolving their linker dependencies ok? And that, for some reason, libavcodec.so and libjniavcodec.so are not resolving those dependencies?
Lastly, is there any way to get insight into what exactly is failing inside the native code? Looks like the ClassNotFoundException that is being thrown there is turned into a string which simply says "Could not initialize class org.bytedeco.javacpp.avcodec". Can I get more info? Are there logs somewhere that I should consult? Are there logging properties I should set? Are there system properties I should enable?
Again, any clues would be greatly appreciated. In the meantime I will try re-downloading the jars.
Also how concerned should I be about these missing dependencies:
[root@7c2cca9eebf8 lib]# ldd libjniavdevice.so ldd: warning: you do not have execution permission for `./libjniavdevice.so' linux-vdso.so.1 => (0x00007ffdaa9cc000) libavdevice.so.57 => /usr/local/lib/./libavdevice.so.57 (0x00007f8d24c08000) libavfilter.so.6 => /usr/local/lib/./libavfilter.so.6 (0x00007f8d247c2000) libswscale.so.4 => /usr/local/lib/./libswscale.so.4 (0x00007f8d24530000) libpostproc.so.54 => /usr/local/lib/./libpostproc.so.54 (0x00007f8d242e8000) libavformat.so.57 => /usr/local/lib/./libavformat.so.57 (0x00007f8d23cb8000) libavcodec.so.57 => /usr/local/lib/./libavcodec.so.57 (0x00007f8d21dc5000) libswresample.so.2 => /usr/local/lib/./libswresample.so.2 (0x00007f8d21ba8000) libavutil.so.55 => /usr/local/lib/./libavutil.so.55 (0x00007f8d21931000) libstdc++.so.6 => /lib64/libstdc++.so.6 (0x00007f8d21626000) libm.so.6 => /lib64/libm.so.6 (0x00007f8d21324000) libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007f8d2110d000) libc.so.6 => /lib64/libc.so.6 (0x00007f8d20d4c000) libXfixes.so.3 => not found libXext.so.6 => not found libX11.so.6 => not found libasound.so.2 => not found libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f8d20b2e000) libdl.so.2 => /lib64/libdl.so.2 (0x00007f8d2092a000) librt.so.1 => /lib64/librt.so.1 (0x00007f8d20721000) /lib64/ld-linux-x86-64.so.2 (0x000055d8e17c9000)
Like I said, the JDK will tell us what is the cause of the error. The fact that you don't get anything points to a problem with your version of Java or Linux. I can't do anything about that!
To understand more about all this, please go and learn more about shared objects on the Linux platform.
@saudet I'm trying to understand how YOUR library is working. I have a working understanding of shared objects on Linux. But I'm asking you to explain how you expect things to work. I honestly don't know how to get the JDK to tell me anything more than I've provided for you. Can you please tell me how to do that? What more do you expect to see? Why, when I try to get the Cause of the exception is it null/empty? What are you expecting the exception from the native code to tell you? Can you please cut/paste examples of other NoClassDefFoundErrors loading avcodec that you've seen? That might at least give me a place to start looking.
Look, I know this is painful and that I'm taxing you with all these questions. But can you see how frustrating this is from my perspective. The person who, presumably, knows that most about this code and this setup is not being terribly responsive. I want to collaborate with you. I want to provide you with whatever information I can to help us debug this together. Pointing me back at the internet to learn about shared objects is not productive. How can we move forward here?
Here's the kind of stack trace you're supposed to get: https://github.com/bytedeco/javacpp-presets/wiki/Debugging-UnsatisfiedLinkError-on-Windows
You can try to set LD_LIBRARY_PATH before launching the JDK, but that won't help, because that's not where the problem is: avutil loads fine for example.
If you want to figure out what the problem is, first figure out why you're not getting this info from the JDK! Fiddling with .so files probably won't help.
Thank you @saudet. Can you please explain, though, why I'm seeing a NoClassDefFoundError rather than an UnSatisfiedLinkeError? And how are those things different? It seems like the latter is thrown because a shared object simply isn't in the path. I think in this case all the shared objects are there, but the invocation of the avcodec native class is blowing up for some reason.
Maybe we're not getting an UnsatisfiedLinkError as the cause, maybe that's the point. That's what I'm saying we need to know. You could try to consult upstream with the authors of the JDK!
One thing that might help is setting the "org.bytedeco.javacpp.logger.debug" system property to "true". That will print out what the Loader is doing...
In the example you listed here's the stack:
java.lang.UnsatisfiedLinkError: no jniFlyCapture2 in java.library.path
With debug logging:
01:30:22
Loading class org.bytedeco.javacpp.avutil
01:30:22
Loading class org.bytedeco.javacpp.avutil
01:30:22
Locking /root/.javacpp/cache
01:30:22
Extracting jar:file:/usr/bifservice/lib/ffmpeg-3.2.1-1.3-linux-x86_64.jar!/org/bytedeco/javacpp/linux-x86_64/libavutil.so.55
01:30:22
Loading /root/.javacpp/cache/ffmpeg-3.2.1-1.3-linux-x86_64.jar/org/bytedeco/javacpp/linux-x86_64/libavutil.so.55
01:30:22
Creating symbolic link /root/.javacpp/cache/ffmpeg-3.2.1-1.3-linux-x86_64.jar/org/bytedeco/javacpp/linux-x86_64/libavutil.so
01:30:22
Locking /root/.javacpp/cache
01:30:22
Extracting jar:file:/usr/bifservice/lib/ffmpeg-3.2.1-1.3-linux-x86_64.jar!/org/bytedeco/javacpp/linux-x86_64/libjniavutil.so
01:30:22
Loading /root/.javacpp/cache/ffmpeg-3.2.1-1.3-linux-x86_64.jar/org/bytedeco/javacpp/linux-x86_64/libjniavutil.so
01:30:22
Loading class org.bytedeco.javacpp.avutil
01:30:22
Loading class org.bytedeco.javacpp.swresample
01:30:22
Loading class org.bytedeco.javacpp.avutil
01:30:22
Loading class org.bytedeco.javacpp.swresample
01:30:22
Locking /root/.javacpp/cache
01:30:22
Extracting jar:file:/usr/bifservice/lib/ffmpeg-3.2.1-1.3-linux-x86_64.jar!/org/bytedeco/javacpp/linux-x86_64/libswresample.so.2
01:30:22
Loading /root/.javacpp/cache/ffmpeg-3.2.1-1.3-linux-x86_64.jar/org/bytedeco/javacpp/linux-x86_64/libswresample.so.2
01:30:22
Creating symbolic link /root/.javacpp/cache/ffmpeg-3.2.1-1.3-linux-x86_64.jar/org/bytedeco/javacpp/linux-x86_64/libswresample.so
01:30:22
Locking /root/.javacpp/cache
01:30:22
Extracting jar:file:/usr/bifservice/lib/ffmpeg-3.2.1-1.3-linux-x86_64.jar!/org/bytedeco/javacpp/linux-x86_64/libjniswresample.so
01:30:22
Loading /root/.javacpp/cache/ffmpeg-3.2.1-1.3-linux-x86_64.jar/org/bytedeco/javacpp/linux-x86_64/libjniswresample.so
01:30:22
Loading class org.bytedeco.javacpp.avutil
01:30:22
Loading class org.bytedeco.javacpp.swresample
01:30:22
Loading class org.bytedeco.javacpp.avcodec
01:30:22
Loading class org.bytedeco.javacpp.avutil
01:30:22
Loading class org.bytedeco.javacpp.swresample
01:30:22
Loading class org.bytedeco.javacpp.avcodec
01:30:22
Locking /root/.javacpp/cache
01:30:22
Extracting jar:file:/usr/bifservice/lib/ffmpeg-3.2.1-1.3-linux-x86_64.jar!/org/bytedeco/javacpp/linux-x86_64/libavcodec.so.57
01:30:22
Loading /root/.javacpp/cache/ffmpeg-3.2.1-1.3-linux-x86_64.jar/org/bytedeco/javacpp/linux-x86_64/libavcodec.so.57
01:30:22
Creating symbolic link /root/.javacpp/cache/ffmpeg-3.2.1-1.3-linux-x86_64.jar/org/bytedeco/javacpp/linux-x86_64/libavcodec.so
01:30:22
Locking /root/.javacpp/cache
01:30:22
Extracting jar:file:/usr/bifservice/lib/ffmpeg-3.2.1-1.3-linux-x86_64.jar!/org/bytedeco/javacpp/linux-x86_64/libjniavcodec.so
01:30:22
Loading /root/.javacpp/cache/ffmpeg-3.2.1-1.3-linux-x86_64.jar/org/bytedeco/javacpp/linux-x86_64/libjniavcodec.so
01:30:22
Error loading class org/bytedeco/javacpp/Loader.
01:30:24
Loading class org.bytedeco.javacpp.avutil
01:30:24
Loading class org.bytedeco.javacpp.avutil
01:30:24
Loading class org.bytedeco.javacpp.swresample
01:30:24
Loading class org.bytedeco.javacpp.avutil
01:30:24
Loading class org.bytedeco.javacpp.swresample
01:30:24
Loading class org.bytedeco.javacpp.avcodec
01:30:24
2017-11-18T01:30:24,091 | com_roku_bif_video_processing-worker | ERROR | HLSImageSupplier:101 | Problem loading avcodec
01:30:24
java.lang.NoClassDefFoundError: Could not initialize class org.bytedeco.javacpp.avcodec
01:30:24
at java.lang.Class.forName0(Native Method) ~[?:1.8.0_45]
01:30:24
at java.lang.Class.forName(Class.java:348) ~[?:1.8.0_45]
01:30:24
at org.bytedeco.javacpp.Loader.load(Loader.java:739) ~[javacpp-1.3.3.jar:1.3.3]
01:30:24
at org.bytedeco.javacpp.Loader.load(Loader.java:700) ~[javacpp-1.3.3.jar:1.3.3]
01:30:24
at org.bytedeco.javacpp.Loader.load(Loader.java:684) ~[javacpp-1.3.3.jar:1.3.3]
01:30:24
at org.bytedeco.javacpp.avformat$AVFormatContext.
My code after this!
try {
grabber.start();
Frame f = null;
while ((f = grabber.grabKeyFrame()) != null) {
final BufferedImage image = converter.convert(f);
final Double timestamp = Double.valueOf(grabber.getTimestamp() / 1_000_000d);
holder.collectImage(image, timestamp);
}
} catch (final IOException e) {
logger.warn("IOException during frame grabbing", e);
} finally {
try {
grabber.close();
} catch (final Exception e) {
logger.warn("Exception closing frame grabber", e);
}
}
It's failing on a very simple call to get a reference to the Loader in native code: https://github.com/bytedeco/javacpp/blob/master/src/main/java/org/bytedeco/javacpp/tools/Generator.java#L687
Have you tried to change the version of your JDK? If you're stuck with a specific vendor and/or version, then please contact the vendor of your JDK. This doesn't look like a problem with JavaCPP, Linux, or FFmpeg.
Is there a recommended version of the JDK? I can try updating.
Just so I understand then. The root problem is this: Error loading class org/bytedeco/javacpp/Loader.
And that is, in turn, leading to a failure to load avcodec.class? But not leading to a failure to load avutil.class or swresample.class. Is that correct?
Put another way: Why does the Loader class only fail to load AFTER jniavcodec is extracted and loaded?
02:16:06 Locking /root/.javacpp/cache 02:16:06 Extracting jar:file:/usr/bifservice/lib/ffmpeg-3.2.1-1.3-linux-x86_64.jar!/org/bytedeco/javacpp/linux-x86_64/libjniavcodec.so 02:16:06 Loading /root/.javacpp/cache/ffmpeg-3.2.1-1.3-linux-x86_64.jar/org/bytedeco/javacpp/linux-x86_64/libjniavcodec.so 02:16:06 Error loading class org/bytedeco/javacpp/Loader. 02:16:08 Loading class org.bytedeco.javacpp.avutil 02:16:08 Loading class org.bytedeco.javacpp.swresample 02:16:08 Loading class org.bytedeco.javacpp.avcodec
@saudet btw, thank you for the code link. What does this mean:
jclass cls = env->FindClass(JavaCPP_classNames[i]);"); out.println(" if (cls == NULL || env->ExceptionCheck())
that is, under what conditions would "cls" be null or env->ExceptionCheck() be true? Could there be some underlying system constraint issue?
Possibly, never heard of anyone with that issue though.
Ok, I got it that you've not seen this problem before. However, not having seen it before is not a solution and, unfortunately, I need a solution. So, under what condition would env->FindClass return null?
Ok, thanks @saudet I'll take a look at that. In the meantime can you answer this question for me:
I see that avutil, avcodec, and swresample get reloaded every time one of the jars is extracted and loaded. I'm assuming each of those involves an invocation of the Loader class. Is that correct? If so, can you speculate as to what is special about the case where libjniavcodec is extracted and loaded? What I'm getting at is: Is there still potentially a problem with libavcodec and/or one of its dependencies? And could that problem be the cause of the failure to load the Loader class? Or are the two things coincidences?
Might be related to this: https://github.com/bytedeco/javacv/issues/791
This seems promising. I will try it first thing tomorrow morning. Thank you.
Tried increasing the stack size to 1mb and that still didn't work. Also set the number of shadow pages to 20 and that didn't help either. ::sigh::
May not be terribly helpful, but could you tell us what Java runtime you are currently using for completeness sake? (I think java --version
should give enough info)
From the docker file:
ENV JDK_URL http://download.oracle.com/otn-pub/java/jdk/8u91-b14/jdk-8u91-linux-x64.rpm ENV JDK_RPM jdk-8u91-linux-x64.rpm'
Does that help?
I use OpenJDK 8, whatever version works fine. Oracle JDK 8 also works fine, obviously.
Docker containers come with weird bugs and limits. I'd check that first.
I'm attempting to get a setup running outside of Docker. Again, to reiterate, this works just fine on my local Mac OS X. I don't think it's a Docker issue. I really think that there is a missing "Caused By" here and that Caused By will probably tell me that the .so's that I have are not internally consistent. That's the only thing I can think of. Like I said, I tried increasing the stack space and the shadow page space. But to me this feels like the init of the avcodec class is invoking something in libavcodec that is not finding what it needs elsewhere (even though ldd says that all the dependencies are satisfied). Also, in one of my earlier notes I included that the following dependencies were missing:
libXfixes.so.3 => not found libXext.so.6 => not found libX11.so.6 => not found libasound.so.2 => not found
Not sure how important those are. That came from checking "ldd libjniavdevice.so" which seems like it gets loaded AFTER avcodec. So not sure how relevant that is (yet?).
This change might help: https://github.com/bytedeco/javacpp/commit/79680934375a4844d64a026d9b02e58196915c7f
Great, how could I test that out?
Use the 1.3.4-SNAPSHOT artifacts: http://bytedeco.org/builds/
Ok, I haven't tried the snaphshot yet. But I DO HAVE IT WORKING on a Centos 7 Docker image! I had previously got it working on a Centos 7 VM using Vagrant. I noticed that my max heap was set to 1Gb. That resulted in a bunch of OOM errors that did not make it to my logs in AWS but were on the console. So I increased my Docker memory requirement and max heap both to 3Gb. I also followed the advice of one of the other issues mentioned earlier and bumped up my stack size (-Xss2m) as well as set the shadow page size to 20. With these settings I now get the output that I expect! I do see some debug messages from javacpp saying that libXfixes.so is missing. But that doesn't seem to affect my output. In any case this is great and gives me a path forward. At some point in the future I'll try returning to Alpine Linux with the afore mentioned settings and see if that works as well.
Thanks for your help on this.
1 GB is pretty small, so you were just running out of memory it seems.
In any case, great to hear it's working well now!
Sorry for the multiple posts but I'm really trying to get some help resolving this issue. I'm trying to use the FFmpegFrameGrabber class on an Alpine Linux system in order to do some frame grabs from videos (hls and mp4 format). I continue to run into the following error:
Error loading class org/bytedeco/javacpp/Loader. java.lang.NoClassDefFoundError: Could not initialize class org.bytedeco.javacpp.avcodec at java.lang.Class.forName0(Native Method) ~[?:1.8.0_131] at java.lang.Class.forName(Class.java:348) ~[?:1.8.0_131] at org.bytedeco.javacpp.Loader.load(Loader.java:739) ~[javacpp-1.3.3.jar:1.3.3] at org.bytedeco.javacpp.Loader.load(Loader.java:684) ~[javacpp-1.3.3.jar:1.3.3] at org.bytedeco.javacpp.avformat$AVFormatContext.(avformat.java:2819) ~[ffmpeg-3.2.1-1.3.jar:1.3.3]
at org.bytedeco.javacv.FFmpegFrameGrabber.startUnsafe(FFmpegFrameGrabber.java:509) ~[javacv-1.3.3.jar:1.3.3]
at org.bytedeco.javacv.FFmpegFrameGrabber.start(FFmpegFrameGrabber.java:503) ~[javacv-1.3.3.jar:1.3.3]