kenversion / javacv

Automatically exported from code.google.com/p/javacv
GNU General Public License v2.0
0 stars 0 forks source link

Clear documentation on how to use callbacks from FFMpeg and OpenCV in JavaCV using JavaCPP #322

Open GoogleCodeExporter opened 8 years ago

GoogleCodeExporter commented 8 years ago
What steps will reproduce the problem?
1. Enhancement
2.
3.

What is the expected output? What do you see instead?
Easy to follow steps, and an example on how to implement callbacks in JavaCV 
without adding a separate JNI implementation

What version of the product are you using? On what operating system?
0.5 / android

Please provide any additional information below.

Original issue reported on code.google.com by klomm...@gmail.com on 26 May 2013 at 6:11

GoogleCodeExporter commented 8 years ago
The documentation from JavaCPP should provide some insights:
    http://code.google.com/p/javacpp/#Creating_Callback_Functions
As to what you need to do specifically, do you have some code in C that we 
could take a look at?

Original comment by samuel.a...@gmail.com on 27 May 2013 at 3:32

GoogleCodeExporter commented 8 years ago
Hey Samuel,

I have the following code:

Java
-----

import com.googlecode.javacpp.FunctionPointer;
import com.googlecode.javacpp.Loader;
import com.googlecode.javacpp.annotation.Name;
import com.googlecode.javacpp.annotation.Namespace;
import com.googlecode.javacpp.annotation.Platform;

import android.util.Log;

@Platform(include="<algorithm>")
@Namespace("std")
public class FFMpegCallbackManager {

    private final static String CLASS_LABEL = "VideoView";
    private final static String LOG_TAG = CLASS_LABEL;

    static { Loader.load(); }

    public static class Callback extends FunctionPointer {
        // Loader.load() and allocate() are required only when explicitly creating an instance
        static { Loader.load(); }
        protected Callback() { allocate(); }
        private native void allocate();

        public @Name("onRTMPMessage") void call(String messageType, String messageContent) throws Exception { 
            Log.w(LOG_TAG,"onRTMPMessage: " + messageType + ": " + messageContent);
        }
    }
}

-----

C

In avformat/rtmpproto.c @ handle_notify

        datatowrite       = gbc.buffer;
        datatowritelength = bytestream2_get_bytes_left(&gbc);

    gbc.buffer += 11;

    if (ff_amf_read_string(&gbc, statusmsg, sizeof(statusmsg), &stringlen)) {
            av_log(s, AV_LOG_ERROR, "onCuePoint Failed\n");
            return AVERROR_INVALIDDATA;
    } else {                    
            JavaCPP_init(0, NULL);
            onRTMPMessage("messageType", "messageContent");
            JavaCPP_uninit();
        }

---

I looked at the example before writing, and couldn't figure out in the example 
what's in jniFoo.h, and where it comes from. It didn't seem to me it was 
produced by javah

Moreover, when i take the step of preparing the instructions for android (in 
windows) i get the following exceptions:

Warning: Could not load class classes.com.younow.adapters.YouNowChannelAdapter:
java.lang.NoClassDefFoundError: classes/com/younow/adapters/YouNowChannelAdapter
 (wrong name: com/younow/adapters/YouNowChannelAdapter)
Warning: Could not load class classes.com.younow.adapters.YouNowEnvAdapter: java
.lang.NoClassDefFoundError: classes/com/younow/adapters/YouNowEnvAdapter (wrong
name: com/younow/adapters/YouNowEnvAdapter)
Warning: Could not load class classes.com.younow.adapters.YouNowSourceAdapter: j
ava.lang.NoClassDefFoundError: classes/com/younow/adapters/YouNowSourceAdapter (
wrong name: com/younow/adapters/YouNowSourceAdapter)
Warning: Could not load class classes.com.younow.BuildConfig: java.lang.NoClassD
efFoundError: classes/com/younow/BuildConfig (wrong name: com/younow/BuildConfig
)
Warning: Could not load class classes.com.younow.jni.FFMpegCallbackManager$Callb
ack: java.lang.NoClassDefFoundError: classes/com/younow/jni/FFMpegCallbackManage
r$Callback (wrong name: com/younow/jni/FFMpegCallbackManager$Callback)
Warning: Could not load class classes.com.younow.jni.FFMpegCallbackManager: java
.lang.NoClassDefFoundError: classes/com/younow/jni/FFMpegCallbackManager (wrong
name: com/younow/jni/FFMpegCallbackManager)
Warning: Could not load class classes.com.younow.MainActivity$1: java.lang.NoCla
ssDefFoundError: classes/com/younow/MainActivity$1 (wrong name: com/younow/MainA
ctivity$1)
Warning: Could not load class classes.com.younow.MainActivity$2: java.lang.NoCla
ssDefFoundError: classes/com/younow/MainActivity$2 (wrong name: com/younow/MainA
ctivity$2)
Warning: Could not load class classes.com.younow.MainActivity$3: java.lang.NoCla
ssDefFoundError: classes/com/younow/MainActivity$3 (wrong name: com/younow/MainA
ctivity$3)
Warning: Could not load class classes.com.younow.MainActivity: java.lang.NoClass
DefFoundError: classes/com/younow/MainActivity (wrong name: com/younow/MainActiv
ity)
Warning: Could not load class classes.com.younow.net.YouNowHttpClient: java.lang
.NoClassDefFoundError: classes/com/younow/net/YouNowHttpClient (wrong name: com/
younow/net/YouNowHttpClient)
Warning: Could not load class classes.com.younow.R$attr: java.lang.NoClassDefFou
ndError: classes/com/younow/R$attr (wrong name: com/younow/R$attr)
Warning: Could not load class classes.com.younow.R$dimen: java.lang.NoClassDefFo
undError: classes/com/younow/R$dimen (wrong name: com/younow/R$dimen)
Warning: Could not load class classes.com.younow.R$drawable: java.lang.NoClassDe
fFoundError: classes/com/younow/R$drawable (wrong name: com/younow/R$drawable)
Warning: Could not load class classes.com.younow.R$id: java.lang.NoClassDefFound
Error: classes/com/younow/R$id (wrong name: com/younow/R$id)
Warning: Could not load class classes.com.younow.R$layout: java.lang.NoClassDefF
oundError: classes/com/younow/R$layout (wrong name: com/younow/R$layout)
Warning: Could not load class classes.com.younow.R$menu: java.lang.NoClassDefFou
ndError: classes/com/younow/R$menu (wrong name: com/younow/R$menu)
Warning: Could not load class classes.com.younow.R$string: java.lang.NoClassDefF
oundError: classes/com/younow/R$string (wrong name: com/younow/R$string)
Warning: Could not load class classes.com.younow.R$style: java.lang.NoClassDefFo
undError: classes/com/younow/R$style (wrong name: com/younow/R$style)
Warning: Could not load class classes.com.younow.R: java.lang.NoClassDefFoundErr
or: classes/com/younow/R (wrong name: com/younow/R)
Warning: Could not load class classes.com.younow.RecordActivity$AudioRecordRunna
ble: java.lang.NoClassDefFoundError: classes/com/younow/RecordActivity$AudioReco
rdRunnable (wrong name: com/younow/RecordActivity$AudioRecordRunnable)
Warning: Could not load class classes.com.younow.RecordActivity$CameraView: java
.lang.NoClassDefFoundError: classes/com/younow/RecordActivity$CameraView (wrong
name: com/younow/RecordActivity$CameraView)
Warning: Could not load class classes.com.younow.RecordActivity: java.lang.NoCla
ssDefFoundError: classes/com/younow/RecordActivity (wrong name: com/younow/Recor
dActivity)
Warning: Could not load class classes.com.younow.server.YouNowChannel: java.lang
.NoClassDefFoundError: classes/com/younow/server/YouNowChannel (wrong name: com/
younow/server/YouNowChannel)
Warning: Could not load class classes.com.younow.server.YouNowEnv: java.lang.NoC
lassDefFoundError: classes/com/younow/server/YouNowEnv (wrong name: com/younow/s
erver/YouNowEnv)
Warning: Could not load class classes.com.younow.server.YouNowSource: java.lang.
NoClassDefFoundError: classes/com/younow/server/YouNowSource (wrong name: com/yo
unow/server/YouNowSource)
Warning: Could not load class classes.com.younow.VersionActivity: java.lang.NoCl
assDefFoundError: classes/com/younow/VersionActivity (wrong name: com/younow/Ver
sionActivity)
Warning: Could not load class classes.com.younow.ViewActivity$1: java.lang.NoCla
ssDefFoundError: classes/com/younow/ViewActivity$1 (wrong name: com/younow/ViewA
ctivity$1)
Warning: Could not load class classes.com.younow.ViewActivity$2: java.lang.NoCla
ssDefFoundError: classes/com/younow/ViewActivity$2 (wrong name: com/younow/ViewA
ctivity$2)
Warning: Could not load class classes.com.younow.ViewActivity$3: java.lang.NoCla
ssDefFoundError: classes/com/younow/ViewActivity$3 (wrong name: com/younow/ViewA
ctivity$3)
Warning: Could not load class classes.com.younow.ViewActivity$4$1: java.lang.NoC
lassDefFoundError: classes/com/younow/ViewActivity$4$1 (wrong name: com/younow/V
iewActivity$4$1)
Warning: Could not load class classes.com.younow.ViewActivity$4: java.lang.NoCla
ssDefFoundError: classes/com/younow/ViewActivity$4 (wrong name: com/younow/ViewA
ctivity$4)
Warning: Could not load class classes.com.younow.ViewActivity$DoorsDataResponse:
 java.lang.NoClassDefFoundError: classes/com/younow/ViewActivity$DoorsDataRespon
se (wrong name: com/younow/ViewActivity$DoorsDataResponse)
Warning: Could not load class classes.com.younow.ViewActivity$DoorsDataRetriever
: java.lang.NoClassDefFoundError: classes/com/younow/ViewActivity$DoorsDataRetri
ever (wrong name: com/younow/ViewActivity$DoorsDataRetriever)
Warning: Could not load class classes.com.younow.ViewActivity: java.lang.NoClass
DefFoundError: classes/com/younow/ViewActivity (wrong name: com/younow/ViewActiv
ity)
Warning: Could not load class classes.com.younow.views.VideoView$CanvasThread: j
ava.lang.NoClassDefFoundError: classes/com/younow/views/VideoView$CanvasThread (
wrong name: com/younow/views/VideoView$CanvasThread)
Warning: Could not load class classes.com.younow.views.VideoView: java.lang.NoCl
assDefFoundError: classes/com/younow/views/VideoView (wrong name: com/younow/vie
ws/VideoView)
Warning: Could not load class com.younow.adapters.YouNowChannelAdapter: java.lan
g.NoClassDefFoundError: android/widget/ArrayAdapter
Warning: Could not load class com.younow.adapters.YouNowEnvAdapter: java.lang.No
ClassDefFoundError: android/widget/ArrayAdapter
Warning: Could not load class com.younow.adapters.YouNowSourceAdapter: java.lang
.NoClassDefFoundError: android/widget/ArrayAdapter
Warning: Could not load class com.younow.MainActivity$1: java.lang.NoClassDefFou
ndError: android/view/View$OnClickListener
Warning: Could not load class com.younow.MainActivity$2: java.lang.NoClassDefFou
ndError: android/view/View$OnClickListener
Warning: Could not load class com.younow.MainActivity$3: java.lang.NoClassDefFou
ndError: android/view/View$OnClickListener
Warning: Could not load class com.younow.MainActivity: java.lang.NoClassDefFound
Error: android/app/Activity
Warning: Could not load class com.younow.RecordActivity$CameraView: java.lang.No
ClassDefFoundError: android/view/SurfaceHolder$Callback
Warning: Could not load class com.younow.RecordActivity: java.lang.NoClassDefFou
ndError: android/view/View$OnClickListener
Warning: Could not load class com.younow.VersionActivity: java.lang.NoClassDefFo
undError: android/app/Activity
Warning: Could not load class com.younow.ViewActivity$1: java.lang.NoClassDefFou
ndError: android/widget/AdapterView$OnItemSelectedListener
Warning: Could not load class com.younow.ViewActivity$2: java.lang.NoClassDefFou
ndError: android/widget/AdapterView$OnItemSelectedListener
Warning: Could not load class com.younow.ViewActivity$3: java.lang.NoClassDefFou
ndError: android/widget/AdapterView$OnItemSelectedListener
Warning: Could not load class com.younow.ViewActivity$DoorsDataRetriever: java.l
ang.NoClassDefFoundError: android/os/AsyncTask
Warning: Could not load class com.younow.ViewActivity: java.lang.NoClassDefFound
Error: android/view/View$OnClickListener
Warning: Could not load class com.younow.views.VideoView: java.lang.NoClassDefFo
undError: android/view/SurfaceHolder$Callback
Exception in thread "main" java.lang.NoClassDefFoundError: android/view/View$OnC
lickListener
        at java.lang.ClassLoader.defineClass1(Native Method)
        at java.lang.ClassLoader.defineClass(Unknown Source)
        at java.security.SecureClassLoader.defineClass(Unknown Source)
        at java.net.URLClassLoader.defineClass(Unknown Source)
        at java.net.URLClassLoader.access$100(Unknown Source)
        at java.net.URLClassLoader$1.run(Unknown Source)
        at java.net.URLClassLoader$1.run(Unknown Source)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(Unknown Source)
        at com.googlecode.javacpp.Builder$UserClassLoader.findClass(Builder.java
:479)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        at java.lang.Class.getDeclaringClass(Native Method)
        at com.googlecode.javacpp.Loader.appendProperties(Loader.java:158)
        at com.googlecode.javacpp.Builder.build(Builder.java:744)
        at com.googlecode.javacpp.Builder.main(Builder.java:861)
Caused by: java.lang.ClassNotFoundException: android.view.View$OnClickListener
        at java.net.URLClassLoader$1.run(Unknown Source)
        at java.net.URLClassLoader$1.run(Unknown Source)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(Unknown Source)
        at com.googlecode.javacpp.Builder$UserClassLoader.findClass(Builder.java
:479)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        ... 16 more

Thanks a lot,
K

Original comment by klomm...@gmail.com on 27 May 2013 at 4:08

GoogleCodeExporter commented 8 years ago
I forgot to mention one thing - I also have no idea where it comes from. I 
searched the projects of JavaCV and JavaCPP searching for it, and found nothing.

p.s.
I truly appreciate the effort you take into answering my ignorant questions. 
Thanks again.

Original comment by klomm...@gmail.com on 27 May 2013 at 4:29

GoogleCodeExporter commented 8 years ago
It seems JavaCPP can't find your classes there. Have you made sure to include 
everything in the CLASSPATH? Or try to decouple your "FFMpegCallbackManager" 
from the rest of the code?

jniFoo.h comes from the "-header" command line option. Try it out as per the 
README.txt file!

Original comment by samuel.a...@gmail.com on 2 Jun 2013 at 5:38

GoogleCodeExporter commented 8 years ago
Hey Samuel, 

Thank you so much for your guidance. I'll try it again and update on what i 
came up with. 

Much appreciated,
K

Original comment by klomm...@gmail.com on 2 Jun 2013 at 12:23

GoogleCodeExporter commented 8 years ago
Hey Samuel,

Well, I made a small progress - was able to create the header and the .so lib 
file for FFMpegCallbackManager, and added the change to the C code (attached). 
Built FFMpeg and JavaCV successfully with the new changes, and tried the 
application. Received the following exceptions:

06-03 13:43:09.745: E/AndroidRuntime(1020): FATAL EXCEPTION: main
06-03 13:43:09.745: E/AndroidRuntime(1020): 
java.lang.ExceptionInInitializerError
06-03 13:43:09.745: E/AndroidRuntime(1020):     at 
com.googlecode.javacv.FFmpegFrameGrabber.<init>(FFmpegFrameGrabber.java:105)
06-03 13:43:09.745: E/AndroidRuntime(1020):     at 
com.younow.views.VideoView.initGrabber(VideoView.java:110)
06-03 13:43:09.745: E/AndroidRuntime(1020):     at 
com.younow.views.VideoView.start(VideoView.java:96)
06-03 13:43:09.745: E/AndroidRuntime(1020):     at 
com.younow.ViewActivity.startVideo(ViewActivity.java:426)
06-03 13:43:09.745: E/AndroidRuntime(1020):     at 
com.younow.ViewActivity.onClick(ViewActivity.java:400)
06-03 13:43:09.745: E/AndroidRuntime(1020):     at 
android.view.View.performClick(View.java:4232)
06-03 13:43:09.745: E/AndroidRuntime(1020):     at 
android.view.View$PerformClick.run(View.java:17298)
06-03 13:43:09.745: E/AndroidRuntime(1020):     at 
android.os.Handler.handleCallback(Handler.java:615)
06-03 13:43:09.745: E/AndroidRuntime(1020):     at 
android.os.Handler.dispatchMessage(Handler.java:92)
06-03 13:43:09.745: E/AndroidRuntime(1020):     at 
android.os.Looper.loop(Looper.java:137)
06-03 13:43:09.745: E/AndroidRuntime(1020):     at 
android.app.ActivityThread.main(ActivityThread.java:4921)
06-03 13:43:09.745: E/AndroidRuntime(1020):     at 
java.lang.reflect.Method.invokeNative(Native Method)
06-03 13:43:09.745: E/AndroidRuntime(1020):     at 
java.lang.reflect.Method.invoke(Method.java:511)
06-03 13:43:09.745: E/AndroidRuntime(1020):     at 
com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1027)
06-03 13:43:09.745: E/AndroidRuntime(1020):     at 
com.android.internal.os.ZygoteInit.main(ZygoteInit.java:794)
06-03 13:43:09.745: E/AndroidRuntime(1020):     at 
dalvik.system.NativeStart.main(Native Method)
06-03 13:43:09.745: E/AndroidRuntime(1020): Caused by: 
java.lang.ExceptionInInitializerError
06-03 13:43:09.745: E/AndroidRuntime(1020):     at 
java.lang.Class.classForName(Native Method)
06-03 13:43:09.745: E/AndroidRuntime(1020):     at 
java.lang.Class.forName(Class.java:217)
06-03 13:43:09.745: E/AndroidRuntime(1020):     at 
com.googlecode.javacpp.Loader.load(Loader.java:453)
06-03 13:43:09.745: E/AndroidRuntime(1020):     at 
com.googlecode.javacv.cpp.avdevice.<clinit>(avdevice.java:62)
06-03 13:43:09.745: E/AndroidRuntime(1020):     ... 16 more
06-03 13:43:09.745: E/AndroidRuntime(1020): Caused by: 
java.lang.ExceptionInInitializerError
06-03 13:43:09.745: E/AndroidRuntime(1020):     at 
java.lang.Class.classForName(Native Method)
06-03 13:43:09.745: E/AndroidRuntime(1020):     at 
java.lang.Class.forName(Class.java:217)
06-03 13:43:09.745: E/AndroidRuntime(1020):     at 
com.googlecode.javacpp.Loader.load(Loader.java:453)
06-03 13:43:09.745: E/AndroidRuntime(1020):     at 
com.googlecode.javacv.cpp.avfilter.<clinit>(avfilter.java:79)
06-03 13:43:09.745: E/AndroidRuntime(1020):     ... 20 more
06-03 13:43:09.745: E/AndroidRuntime(1020): Caused by: 
java.lang.UnsatisfiedLinkError: Cannot load library: link_image[1892]:  1837 
could not load needed library 'libavformat.so' for 'libjniavformat.so' 
(find_library[1202]:  1837 'libavformat.so' failed to load previously)
06-03 13:43:09.745: E/AndroidRuntime(1020):     at 
java.lang.Runtime.loadLibrary(Runtime.java:370)
06-03 13:43:09.745: E/AndroidRuntime(1020):     at 
java.lang.System.loadLibrary(System.java:535)
06-03 13:43:09.745: E/AndroidRuntime(1020):     at 
com.googlecode.javacpp.Loader.loadLibrary(Loader.java:593)
06-03 13:43:09.745: E/AndroidRuntime(1020):     at 
com.googlecode.javacpp.Loader.load(Loader.java:489)
06-03 13:43:09.745: E/AndroidRuntime(1020):     at 
com.googlecode.javacpp.Loader.load(Loader.java:431)
06-03 13:43:09.745: E/AndroidRuntime(1020):     at 
com.googlecode.javacv.cpp.avformat.<clinit>(avformat.java:76)
06-03 13:43:09.745: E/AndroidRuntime(1020):     ... 24 more
06-03 13:43:09.745: E/AndroidRuntime(1020): Caused by: 
java.lang.UnsatisfiedLinkError: Cannot load library: reloc_library[1307]:  1837 
cannot locate 'JavaCPP_init'...
06-03 13:43:09.745: E/AndroidRuntime(1020):     at 
java.lang.Runtime.loadLibrary(Runtime.java:370)
06-03 13:43:09.745: E/AndroidRuntime(1020):     at 
java.lang.System.loadLibrary(System.java:535)
06-03 13:43:09.745: E/AndroidRuntime(1020):     at 
com.googlecode.javacpp.Loader.loadLibrary(Loader.java:593)
06-03 13:43:09.745: E/AndroidRuntime(1020):     at 
com.googlecode.javacpp.Loader.load(Loader.java:481)
06-03 13:43:09.745: E/AndroidRuntime(1020):     ... 26 more

Needless to say - the ffmpeg and jni libs are in armeabi folder. Have no idea 
why it's happening. Any ideas?

K

Original comment by klomm...@gmail.com on 3 Jun 2013 at 2:37

Attachments:

GoogleCodeExporter commented 8 years ago
JavaCPP_init() is basically a simple wrapper to JNI_CreateJavaVM(), but that's 
not available on Android, so just remove the calls to JavaCPP_init() and 
JavaCPP_uninit().

Original comment by samuel.a...@gmail.com on 4 Jun 2013 at 1:19

GoogleCodeExporter commented 8 years ago
No good. After removing the JavaCPP_(un)init lines, it's throwing the same 
exception, this time for onRTMPMessage

06-04 09:03:04.038: E/AndroidRuntime(1501): FATAL EXCEPTION: main
06-04 09:03:04.038: E/AndroidRuntime(1501): 
java.lang.ExceptionInInitializerError
06-04 09:03:04.038: E/AndroidRuntime(1501):     at 
com.googlecode.javacv.FFmpegFrameGrabber.<init>(FFmpegFrameGrabber.java:105)
06-04 09:03:04.038: E/AndroidRuntime(1501):     at 
com.younow.views.VideoView.initGrabber(VideoView.java:110)
06-04 09:03:04.038: E/AndroidRuntime(1501):     at 
com.younow.views.VideoView.start(VideoView.java:96)
06-04 09:03:04.038: E/AndroidRuntime(1501):     at 
com.younow.ViewActivity.startVideo(ViewActivity.java:426)
06-04 09:03:04.038: E/AndroidRuntime(1501):     at 
com.younow.ViewActivity.onClick(ViewActivity.java:400)
06-04 09:03:04.038: E/AndroidRuntime(1501):     at 
android.view.View.performClick(View.java:4232)
06-04 09:03:04.038: E/AndroidRuntime(1501):     at 
android.view.View$PerformClick.run(View.java:17298)
06-04 09:03:04.038: E/AndroidRuntime(1501):     at 
android.os.Handler.handleCallback(Handler.java:615)
06-04 09:03:04.038: E/AndroidRuntime(1501):     at 
android.os.Handler.dispatchMessage(Handler.java:92)
06-04 09:03:04.038: E/AndroidRuntime(1501):     at 
android.os.Looper.loop(Looper.java:137)
06-04 09:03:04.038: E/AndroidRuntime(1501):     at 
android.app.ActivityThread.main(ActivityThread.java:4921)
06-04 09:03:04.038: E/AndroidRuntime(1501):     at 
java.lang.reflect.Method.invokeNative(Native Method)
06-04 09:03:04.038: E/AndroidRuntime(1501):     at 
java.lang.reflect.Method.invoke(Method.java:511)
06-04 09:03:04.038: E/AndroidRuntime(1501):     at 
com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1027)
06-04 09:03:04.038: E/AndroidRuntime(1501):     at 
com.android.internal.os.ZygoteInit.main(ZygoteInit.java:794)
06-04 09:03:04.038: E/AndroidRuntime(1501):     at 
dalvik.system.NativeStart.main(Native Method)
06-04 09:03:04.038: E/AndroidRuntime(1501): Caused by: 
java.lang.ExceptionInInitializerError
06-04 09:03:04.038: E/AndroidRuntime(1501):     at 
java.lang.Class.classForName(Native Method)
06-04 09:03:04.038: E/AndroidRuntime(1501):     at 
java.lang.Class.forName(Class.java:217)
06-04 09:03:04.038: E/AndroidRuntime(1501):     at 
com.googlecode.javacpp.Loader.load(Loader.java:453)
06-04 09:03:04.038: E/AndroidRuntime(1501):     at 
com.googlecode.javacv.cpp.avdevice.<clinit>(avdevice.java:62)
06-04 09:03:04.038: E/AndroidRuntime(1501):     ... 16 more
06-04 09:03:04.038: E/AndroidRuntime(1501): Caused by: 
java.lang.ExceptionInInitializerError
06-04 09:03:04.038: E/AndroidRuntime(1501):     at 
java.lang.Class.classForName(Native Method)
06-04 09:03:04.038: E/AndroidRuntime(1501):     at 
java.lang.Class.forName(Class.java:217)
06-04 09:03:04.038: E/AndroidRuntime(1501):     at 
com.googlecode.javacpp.Loader.load(Loader.java:453)
06-04 09:03:04.038: E/AndroidRuntime(1501):     at 
com.googlecode.javacv.cpp.avfilter.<clinit>(avfilter.java:79)
06-04 09:03:04.038: E/AndroidRuntime(1501):     ... 20 more
06-04 09:03:04.038: E/AndroidRuntime(1501): Caused by: 
java.lang.UnsatisfiedLinkError: Cannot load library: link_image[1892]:  1837 
could not load needed library 'libavformat.so' for 'libjniavformat.so' 
(find_library[1202]:  1837 'libavformat.so' failed to load previously)
06-04 09:03:04.038: E/AndroidRuntime(1501):     at 
java.lang.Runtime.loadLibrary(Runtime.java:370)
06-04 09:03:04.038: E/AndroidRuntime(1501):     at 
java.lang.System.loadLibrary(System.java:535)
06-04 09:03:04.038: E/AndroidRuntime(1501):     at 
com.googlecode.javacpp.Loader.loadLibrary(Loader.java:593)
06-04 09:03:04.038: E/AndroidRuntime(1501):     at 
com.googlecode.javacpp.Loader.load(Loader.java:489)
06-04 09:03:04.038: E/AndroidRuntime(1501):     at 
com.googlecode.javacpp.Loader.load(Loader.java:431)
06-04 09:03:04.038: E/AndroidRuntime(1501):     at 
com.googlecode.javacv.cpp.avformat.<clinit>(avformat.java:76)
06-04 09:03:04.038: E/AndroidRuntime(1501):     ... 24 more
06-04 09:03:04.038: E/AndroidRuntime(1501): Caused by: 
java.lang.UnsatisfiedLinkError: Cannot load library: reloc_library[1307]:  1837 
cannot locate 'onRTMPMessage'...
06-04 09:03:04.038: E/AndroidRuntime(1501):     at 
java.lang.Runtime.loadLibrary(Runtime.java:370)
06-04 09:03:04.038: E/AndroidRuntime(1501):     at 
java.lang.System.loadLibrary(System.java:535)
06-04 09:03:04.038: E/AndroidRuntime(1501):     at 
com.googlecode.javacpp.Loader.loadLibrary(Loader.java:593)
06-04 09:03:04.038: E/AndroidRuntime(1501):     at 
com.googlecode.javacpp.Loader.load(Loader.java:481)
06-04 09:03:04.038: E/AndroidRuntime(1501):     ... 26 more

Original comment by klomm...@gmail.com on 4 Jun 2013 at 6:15

GoogleCodeExporter commented 8 years ago
Ah yes, Android doesn't "officially" support circular dependencies like that, 
so we're probably best avoiding those. The way to go around this would be to 
have an init() function that sets your callback once at start up, something 
like:

public class FFMpegCallbackManager {
    static { Loader.load(); init(callback); }
    public static final Callback callback = new Callback();
    public static native void init(Callback callback);
    public static class Callback extends FunctionPointer {
        static { Loader.load(); }
        public Callback() { allocate(); }
        private native void allocate();

        public void call(String messageType, String messageContent) throws Exception { 
            System.out.println("onRTMPMessage: " + messageType + ": " + messageContent);
        }
    }
}

Where on the native side the init() function stores the function pointer it 
gets in some global variable.

Original comment by samuel.a...@gmail.com on 6 Jun 2013 at 10:03

GoogleCodeExporter commented 8 years ago
Samuel,

Sorry for the late reply, have relocated to NY, and suffering from a killer jet 
lag. I took a look now at what you've written, which makes a lot of sense. I 
have made the proper changes to FFMpegCallbackManager, and built the .so and .h 
files using JavaCPP. 

To my surprise, I have also received a rather exciting .cpp file, which left me 
puzzling. I am not sure what I am supposed to do with this file. Clearly, I 
can't include it in the FFMpeg project. Is it just an example, or is it a must? 
Should I cannibalize it, and take only what I need?

I apologize for my noob questions, but this is getting more complicated for me 
by the minute.

Thanks again.

Original comment by klomm...@gmail.com on 10 Jun 2013 at 10:17

GoogleCodeExporter commented 8 years ago
If you're left with a .cpp file it's either because you used the "-nocompile" 
flag or you're getting a compilation error, and JavaCPP is leaving the file 
behind for your inspection. If you're getting a compiler error, please paste it 
here! thanks

Original comment by samuel.a...@gmail.com on 13 Jun 2013 at 7:02

GoogleCodeExporter commented 8 years ago
It was impossible to notice the error inside all the verbose print out. But 
this is what i get:

libs\armeabi\jniFFMpegCallbackManager.cpp:215:38: warning: missing terminating "
 character [enabled by default]
libs\armeabi\jniFFMpegCallbackManager.cpp: In function 'void Java_com_younow_jni
_FFMpegCallbackManager_init(JNIEnv*, jclass, jobject)':
libs\armeabi\jniFFMpegCallbackManager.cpp:1165:47: error: 'init' was not 
declaredd in this scope

I am attaching everything involved.

Thanks

Original comment by klomm...@gmail.com on 13 Jun 2013 at 1:55

Attachments:

GoogleCodeExporter commented 8 years ago
init() is a native function you need to provide in your code somewhere, 
including a function declaration in a header file. And I guess we would put it 
along your modifications in rtmpproto.c ...

Original comment by samuel.a...@gmail.com on 16 Jun 2013 at 2:10

GoogleCodeExporter commented 8 years ago
That is clear - and i have everything prepared (or so i think)

I have these lines in the header (rtmpproto.h) which i prepared, that is 
included in rtmpproto.c :

#include "jniFFMpegCallbackManager.h"

// pointer to the callback function
void (*youNowCallbackPtr)(const char*, const char*);

// init for setting the callback from java
void init(void (*callbackPtr)(const char*, const char*));

question is how do i target javacpp.jar to actually compile the ffmpeg files 
for the .so library. Right now the compilation breaks in the middle because it 
can't find init(). Do i have to include the entire ffmpeg code in the 
compilation of the .so file? 

I don't know if anyone else is following our correspondence, but i will try to 
summarize the entire process we've been through, because at least in my 
opinion, setting up javacpp callbacks to work in android is far from trivial.

Thanks,
K

Original comment by klomm...@gmail.com on 19 Jun 2013 at 9:26

GoogleCodeExporter commented 8 years ago
You can link all that in whatever way works with your platform. With Android, 
I'm pretty sure it works the same as normal Linux in that case:
    1. Define a function
    2. Build a library containing that function definition (the .so file)
    3. Link with that library (yes, the "link" value of JavaCPP means that)

I'm not seeing any function definition from your description, so if you forgot 
that, define it, and try again.

I agree, we need more documentation. Unfortunately, no volunteers has come 
forth. I can give you Wiki access to write up something when you feel up to it.

Original comment by samuel.a...@gmail.com on 21 Jun 2013 at 2:45

GoogleCodeExporter commented 8 years ago
Big progress. This is what the java class looks like:

@Platform(value="android",cinclude="rtmpproto.h", link="avformat", 
preload={"avutil","avcodec"})
public class FFMpegCallbackManager {
    public static final Callback callback = new Callback();

    static { 
        Loader.load(); 
        init(callback);
    }

    public static native void init(Callback callback);

    public static class Callback extends FunctionPointer {
        static { Loader.load(); }
        public Callback() { allocate(); }
        private native void allocate();

        public void call(String messageType, String messageContent) throws Exception {
            Log.w("Callback", "onRTMPMessage: " + messageType + ": " + messageContent);
        }
    }
}

With the function definition of init() as follows:

static void init(void (*callbackPtr)(const char*, const char*)) {
    younowCallbackPtr = callbackPtr;
}

The Java side is actually capable of finding the native init(). However, when i 
call the actual callback, i get a nasty non descriptive SIGSEGV. I suspected 
the callback pointer was NULL, which it was. Unfortunately this is where i get 
stuck. This is the call to the callback:

if (younowCallbackPtr != NULL) {
    younowCallbackPtr(commandbuffer, statusmsg);                        
}

Wanted to ask if you might have an idea why it could happen, and whether there 
is some kind of built in log interface to pass messages to android because 
right now i have no way of knowing what happens on the native side.

Thanks

Original comment by klomm...@gmail.com on 25 Jun 2013 at 11:15

GoogleCodeExporter commented 8 years ago
i forgot to mention - commandbuffer and statusmsg are both char[]. Just to make 
things clear

Original comment by klomm...@gmail.com on 25 Jun 2013 at 11:17

GoogleCodeExporter commented 8 years ago
I've been debugging a bit further. The callback pointer i'm receiving ISN'T 
null, and when i callback from init() it actually works. The problem begins 
when init is finished, and the pointer that i have copied with much blood and 
sweat is getting destroyed - leading to a null pointer exception.

I'm not entirely sure the problems comes from the C side. Init is a static 
function and saves the pointer in a global variable. Both reside in the same 
file, and are obviously visible to each other. 

I've tried all kind of funky stuff - setting init to non-static, using extern 
and static for the callback pointer, and all other stuff - the simple news is 
that it refuses to work.

It may be interesting to mention that when inside init, i am able to log to 
android using __android_write(), in all other parts of the code, i can't. I 
don't know what to say - i find this all very weird.

Any help would be appreciated.

Code:
The java hasn't changed from the last message

C:
void (*younowCallbackPtr)(const char*, const char*);

static void init(void (*callbackPtr)(const char*, const char*)) {
    char buffer [512];

    sprintf (buffer, "Trying to call younowCallback: %p", callbackPtr);
    __android_log_write(ANDROID_LOG_ERROR, "Tag", buffer);
    __android_log_write(ANDROID_LOG_ERROR, "Tag", "init called");

    callbackPtr("Name", "Value"); // works
    __android_log_write(ANDROID_LOG_ERROR, "Tag", "callback called");

    memcpy ( &younowCallbackPtr, &callbackPtr, sizeof(callbackPtr));        
    __android_log_write(ANDROID_LOG_ERROR, "Tag", buffer);
    younowCallbackPtr("Name", "Value"); //works
}

static int handle_notify(URLContext *s, RTMPPacket *pkt) {
    RTMPContext *rt  = s->priv_data;
    const uint8_t *p = NULL;
    uint8_t *cp      = NULL;
    char commandbuffer[64];
    uint8_t databuffer[512];
    char statusmsg[2048];
    int stringlen;
    GetByteContext gbc;
    PutByteContext pbc;
    uint32_t ts;
    int old_flv_size;
    const uint8_t *datatowrite;
    unsigned datatowritelength;
    uint8_t *data      = NULL;

    p = pkt->data;

    bytestream2_init(&gbc, p, pkt->data_size);
    if (ff_amf_read_string(&gbc, commandbuffer, sizeof(commandbuffer), &stringlen)) {
        return AVERROR_INVALIDDATA;
    }

    if (!strcmp(commandbuffer, "@setDataFrame")) {
        datatowrite       = gbc.buffer;
        datatowritelength = bytestream2_get_bytes_left(&gbc);
        if (ff_amf_read_string(&gbc, statusmsg,
                               sizeof(statusmsg), &stringlen))
            return AVERROR_INVALIDDATA;

        if (strcmp(statusmsg, "onMetaData")) {
            av_log(s, AV_LOG_INFO, "Expecting onMetadata but got %s\n",
                   statusmsg);
            return 0;
        }

        /* Provide ECMAArray to flv */
        ts = pkt->timestamp;

        // generate packet header and put data into buffer for FLV demuxer
        if (rt->flv_off < rt->flv_size) {
            old_flv_size  = rt->flv_size;
            rt->flv_size += datatowritelength + 15;
        } else {
            old_flv_size = 0;
            rt->flv_size = datatowritelength + 15;
            rt->flv_off  = 0;
        }

        cp = av_realloc(rt->flv_data, rt->flv_size);
        if (!cp)
            return AVERROR(ENOMEM);
        rt->flv_data = cp;
        bytestream2_init_writer(&pbc, cp, rt->flv_size);
        bytestream2_skip_p(&pbc, old_flv_size);
        bytestream2_put_byte(&pbc, pkt->type);
        bytestream2_put_be24(&pbc, datatowritelength);
        bytestream2_put_be24(&pbc, ts);
        bytestream2_put_byte(&pbc, ts >> 24);
        bytestream2_put_be24(&pbc, 0);
        bytestream2_put_buffer(&pbc, datatowrite, datatowritelength);
        bytestream2_put_be32(&pbc, 0);
    } else if ((!strcmp(commandbuffer, "onCuePoint")) ||
               (!strcmp(commandbuffer, "onChannelsData"))) {                            

        datatowrite       = gbc.buffer;
        datatowritelength = bytestream2_get_bytes_left(&gbc);

        gbc.buffer += 11;

        if (ff_amf_read_string(&gbc, statusmsg, sizeof(statusmsg), &stringlen)) {
            av_log(s, AV_LOG_ERROR, "onCuePoint Failed\n");
            return AVERROR_INVALIDDATA;
        } else {            
            younowCallbackPtr(commandbuffer, statusmsg); // doesn't work - SIGSEGV at 0x00000000 (code 1)                   
        }
    }

    return 0;
}

Original comment by klomm...@gmail.com on 26 Jun 2013 at 8:11

GoogleCodeExporter commented 8 years ago
It sounds like the `Callback` Java object is getting garbage collected. To 
avoid that, that's why we keep a reference to it in a static variable in your 
FFMpegCallbackManager class there, but for some reason, it is still getting 
garbage collected it seems... Are you sure the FFMpegCallbackManager class 
itself isn't getting garbage collected by whatever classloader Android happens 
to use for it?

Original comment by samuel.a...@gmail.com on 28 Jun 2013 at 11:36

GoogleCodeExporter commented 8 years ago
I think I see what the problem is. The fact that you are using the "static" 
keyword indicates you are putting your code in a header file, so the `include` 
you do for JavaCPP and the one you do for rtmpproto.c (or whatever) ends up 
creating two instances of the global `younowCallbackPtr`: They don't point to 
the same are of memory.

You need to define `younowCallbackPtr` and `init()` in the native library, and 
*link* with it, not *include* it. Java got rid of this nonsense with the 
concept of "import", but C and C++ do not function that way.

Original comment by samuel.a...@gmail.com on 29 Jun 2013 at 2:47

GoogleCodeExporter commented 8 years ago
[deleted comment]
GoogleCodeExporter commented 8 years ago
What you're saying makes sense. The problem is that once i remove the include 
from the @platform annotation, javacpp doesn't seem to be able to find init(). 
I've checked the command line, and it does contain

-lavformat and -L<Path/To/libavformat.so>

So i continued to see avformat's symbol table, and indeed, the function is not 
there. I tried all different combinations, but i can't seem to convince ffmpeg 
to export my function. I get the same results also when running config with 
--disable-stripping. 

The general structure hasn't changed - a non-static function declaration in 
rtmpproto.h, and its definiton in rtmpproto.c.

Original comment by klomm...@gmail.com on 1 Jul 2013 at 5:48

GoogleCodeExporter commented 8 years ago
I'm afraid you will need to learn a bit more about C/C++ on Linux to get this 
working. This page seems to have all the info you need: 
http://web.cs.swarthmore.edu/~newhall/unixhelp/howto_C_libraries.html  If that 
doesn't help, please study some other material.

Original comment by samuel.a...@gmail.com on 2 Jul 2013 at 11:54

GoogleCodeExporter commented 8 years ago
Yeah, you're correct about that. The problem is that FFmpeg is such a huge 
product with its own build process that every guidance is helpful. Been 
experimenting with it yesterday, and still couldnt get it to work. Will make it 
happen.

Telling you all, the moment i am getting this callback to work, we make a huge 
party in japan, and you sir - you get a huge hug for all the help you've given 
me.

Couldn't emphasize enough how much i appreciate that.

K

Original comment by klomm...@gmail.com on 2 Jul 2013 at 12:42

GoogleCodeExporter commented 8 years ago
So, any progress?

You're in Japan? Sure, but I'd prefer contributions to the project :)

Original comment by samuel.a...@gmail.com on 7 Jul 2013 at 1:33

GoogleCodeExporter commented 8 years ago
No. Im in NYC, but I will take a flight to japan just to give u that hug. Didnt 
get anywhere near the code the past week. Has been sick as a dog. The moment I 
have anything ill update.

K

Original comment by klomm...@gmail.com on 7 Jul 2013 at 1:37

GoogleCodeExporter commented 8 years ago
No good news i'm afraid.

Been attempting to take different approaches, but no matter what i tried i 
couldn't get it to work.

My first idea was to create a separate native library that would only have a 
non static init, and extern ynCallbackPtr. Both ffmpeg and the jni library 
would link to it. Didn't work. ynCallbackPtr kept getting different instances 
in different parts of the memory leading to a null pointer exception on the 
native side.

Then i tried to provide only one non-static declaration of init(), using a 
local ynCallbackPtr that doesn't even appear in the header - hoping that it 
would prevent double instances for ynCallbackPtr. I included the header, and 
linked to the native library in java cpp, and provided the init() definition in 
the avformat lib. the jni lib couldn't locate init().

I am still unable to think of a design that will ensure single instance for the 
pointer, and a visible init() declaration both for javacpp and avformat. it 
seems that in javacpp a certain include is a must, and so is a link to some 
library. Same goes on the AVFormat side. I am not sure how i could (as u said) 
link with it, and not include it, when there must be an inclusion on both sides.

I have doubled checked - init() symbol is present on the libavformat.so, but 
without the include annotation, building the jniFFMpegCallbackManager.so fails 
because it can't find init()'s declaration

I thought that since javacv is loading the ffmpeg libs, then their symbols will 
be loaded to the memory, allowing jniFFMpegCallbackManager.so to use these 
tables, but it doesn't work.

Bottom line, i have no idea how to go around this problem. If anyone has an 
idea how to solve this, i would greatly appreciate it. Alas, I give up.

Thanks all.

Original comment by klomm...@gmail.com on 11 Jul 2013 at 9:32

GoogleCodeExporter commented 8 years ago
The problem isn't with the init() function, it's with the variable you use to 
store the function pointer. That variable has to be defined in only one place, 
so you can't put it in any header files.

Original comment by samuel.a...@gmail.com on 12 Jul 2013 at 1:22

GoogleCodeExporter commented 8 years ago
It works!

I don't know whether to cry or laugh, but after seven weeks of on and off 
attempts, and god knows how many bottles of whisky, i found the problem, which 
as expected is rather silly.

My main mistake was to use nm, instead of readelf to check the library symbol 
table. I kept seeing that my callback register function was exported, and 
couldn't understand why JavaCPP can't locate it when loading the library. 
Several minutes of readelf and finally i understood. The function name must 
start with the av prefix in order to be global. That's all.

Samuel, really appreciate all your effort on this. Booking my flight to Japan 
right now! You get a big hug! ;)

Original comment by klomm...@gmail.com on 15 Jul 2013 at 7:01

GoogleCodeExporter commented 8 years ago
Interesting, maybe it's the build system of FFmpeg that removes from the symbol 
table any that doesn't start with "av". Good to know :) Thanks for reporting!

Original comment by samuel.a...@gmail.com on 16 Jul 2013 at 1:43