Wednesday, January 01, 2014

WebRTC app - C# / Xamarin - Part #2 - Attempt #1 - failure to using a JNI .so file directly from C# / Mono

This is a post in a series of postings

  1. WebRTC app - C# / Xamarin - Part #1 - Building platform native webrtc library
  2. WebRTC app - C# / Xamarin - Part #2 - Attempt #1 - failure to using a JNI .so file directly from C# / Mono
  3. WebRTC app - C# / Xamarin - Part #2 - Attempt #2 - success using a JNI .so file from C# / Mono
  4. WebRTC app - C# / Xamarin - (C# - JNI - C/C++) - Summary and GitHub repository
And finally the associated GitHub repository https://github.com/kenneththorman/webrtc-app-mono

In my previous posting WebRTC app - C# / Xamarin - Part #1 - Building platform native webrtc library I have showed how to build a native library that we need to use to build a WebRTC app on Xamarin / Mono.Droid.

In this posting I will take you through my struggles, subsequent failure and the next posting finally success on how to actually use this JNI native library from Mono.Android.

I started a new solution in Visual Studio 2013 and added new Android Application project. Then according to Xamarin: Using Native Libraries.
I needed to add my .so file to the location  
<project>\lib\armeabi-v7a\libwebrtc-video-demo-jni.so.

The next step that I tried was to start using DllImport statements.
using System;
using System.Runtime.InteropServices;
using Android.Content;
using Android.Util;
using Encoding = System.Text.Encoding;

namespace WebRtc
{
        public class ViEAndroidJavaAPI
        {

...
                // API Native

                [DllImport("libwebrtc-video-demo-jni.so")]
                private static extern bool NativeInit(Context context);

                // Video Engine API
                // Initialization and Termination functions
                [DllImport("libwebrtc-video-demo-jni.so")]
                public static extern int GetVideoEngine();

                [DllImport("libwebrtc-video-demo-jni.so")]
                public static extern int Init(bool enableTrace);

                [DllImport("libwebrtc-video-demo-jni.so")]
                public static extern int Terminate();
...


Trying to run my project yielded some EntryPointNotFoundException in the error log. After a bit of Google-ing I found that the method names as seen from Mono are not as you expect instead they contain the full package/class path.

Using the following command on the Ubuntu build machine
~/WebRTCDemo/trunk/webrtc/video_engine/test/android/libs/armeabi-v7a$ arm-linux-androideabi-nm -D libwebrtc-video-demo-jni.so
yielded the following output
00015358 T JNI_OnLoad
00015be4 T Java_org_webrtc_videoengineapp_ViEAndroidJavaAPI_AddRemoteRenderer
00016e3c T Java_org_webrtc_videoengineapp_ViEAndroidJavaAPI_CreateChannel
00015f14 T Java_org_webrtc_videoengineapp_ViEAndroidJavaAPI_EnableNACK
00015f58 T Java_org_webrtc_videoengineapp_ViEAndroidJavaAPI_EnablePLI
00015e04 T Java_org_webrtc_videoengineapp_ViEAndroidJavaAPI_GetCameraOrientation
00015acc T Java_org_webrtc_videoengineapp_ViEAndroidJavaAPI_GetCodecs
000153d4 T Java_org_webrtc_videoengineapp_ViEAndroidJavaAPI_GetVideoEngine
000154cc T Java_org_webrtc_videoengineapp_ViEAndroidJavaAPI_Init
000153d0 T Java_org_webrtc_videoengineapp_ViEAndroidJavaAPI_NativeInit
00015c30 T Java_org_webrtc_videoengineapp_ViEAndroidJavaAPI_RemoveRemoteRenderer
00015fb0 T Java_org_webrtc_videoengineapp_ViEAndroidJavaAPI_SetCallback
00015ea8 T Java_org_webrtc_videoengineapp_ViEAndroidJavaAPI_SetExternalMediaCodecDecoderRenderer
...
So changing my code to the following made the EntryPointNotFoundException go away
using System;
using System.Runtime.InteropServices;
using Android.Content;
using Android.Util;
using Encoding = System.Text.Encoding;

namespace WebRtc
{
        public class ViEAndroidJavaAPI
        {

...
                // API Native

                [DllImport("libwebrtc-video-demo-jni.so")]
                private static extern bool Java_org_webrtc_videoengineapp_ViEAndroidJavaAPI_NativeInit(Context context);

                // Video Engine API
                // Initialization and Termination functions
                [DllImport("libwebrtc-video-demo-jni.so")]
                public static extern int Java_org_webrtc_videoengineapp_ViEAndroidJavaAPI_GetVideoEngine();

                [DllImport("libwebrtc-video-demo-jni.so")]
                public static extern int Java_org_webrtc_videoengineapp_ViEAndroidJavaAPI_Init(bool enableTrace);

                [DllImport("libwebrtc-video-demo-jni.so")]
                public static extern int Java_org_webrtc_videoengineapp_ViEAndroidJavaAPI_Terminate();

...

Now I was faced with another exception which seemed much nastier.
UNHANDLED EXCEPTION: System.Runtime.InteropServices.MarshalDirectiveException: Type Java.Lang.Object which is passed to unmanaged code must have a StructLayout attribute.

12-21 19:29:04.298 I/MonoDroid(15226): UNHANDLED EXCEPTION: System.Runtime.InteropServices.MarshalDirectiveException: Type Java.Lang.Object which is passed to unmanaged code must have a StructLayout attribute.
12-21 19:29:04.298 I/MonoDroid(15226): at (wrapper managed-to-native) WebRtc.ViEAndroidJavaAPI.Java_org_webrtc_videoengineapp_ViEAndroidJavaAPI_NativeInit (Android.Content.Context) 
12-21 19:29:04.298 I/MonoDroid(15226): at WebRtc.ViEAndroidJavaAPI..ctor (Android.Content.Context) [0x00033] in XXX\WebRtc.Mono.Droid\ViEAndroidJavaAPI.cs:30
12-21 19:29:04.298 I/MonoDroid(15226): at WebRtc.Mono.Droid.WebRTCDemo.startMain () [0x0004b] in XXX\WebRtc.Mono.Droid\WebRTCDemo.cs:533
12-21 19:29:04.298 I/MonoDroid(15226): at WebRtc.Mono.Droid.WebRTCDemo.OnCreate (Android.OS.Bundle) [0x0028e] in XXX\WebRtc.Mono.Droid\WebRTCDemo.cs:313
12-21 19:29:04.298 I/MonoDroid(15226): at Android.App.Activity.n_OnCreate_Landroid_os_Bundle_ (intptr,intptr,intptr) [0x00011] in /Users/builder/data/lanes/monodroid-mlion-monodroid-4.10.1-branch/d23a19bf/source/monodroid/src/Mono.Android/platforms/android-17/src/generated/Android.App.Activity.cs:2119
12-21 19:29:04.298 I/MonoDroid(15226): at (wrapper dynamic-method) object.705dc6ba-9c58-4bcd-a8a2-f12584a9175f (intptr,intptr,intptr)

Finally after digging I found this posting Native library integration which basically state
you cannot sanely use P/Invoke to invoke the native method. You must instead use JNI to invoke the Java-side native method.
Basically because this is Java native C/C++ interface we are to invoke, you cannot do this like normal non JNI wrapped C/C++ methods.


This is pushing me in a direction that I initially hoped I could avoid (mainly due to my limited knowledge in the area), JNI.


Later: I did some (quite a bit) reading, namely I found these links useful:

Interop with Native Libraries
Java Integration Overview
Working With JNI

In the next posting in this series I manage to invoke the native methods.

No comments: