/* LinphoneWrapper.cs Copyright (C) 2017 Belledonne Communications SARL This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ using System; using System.Runtime.InteropServices; using System.Collections.Generic; #if __IOS__ using ObjCRuntime; #endif namespace Linphone { #region Wrapper specifics /// /// Only contains the LIB_NAME value that represents the library in which all DllImport are made /// public class LinphoneWrapper { #if __IOS__ public const string LIB_NAME = "linphone.framework/linphone"; #else public const string LIB_NAME = "linphone"; // With this, it automatically finds liblinphone.so #endif #if WINDOWS_UWP public const string BELLE_SIP_LIB_NAME = "bellesip"; public const string BCTOOLBOX_LIB_NAME = "bctoolbox"; #else public const string BELLE_SIP_LIB_NAME = "linphone"; public const string BCTOOLBOX_LIB_NAME = "linphone"; #endif #if ANDROID [DllImport(LinphoneWrapper.LIB_NAME)] static extern void setAndroidLogHandler(); #endif #if __IOS__ [DllImport(LinphoneWrapper.LIB_NAME)] static extern void linphone_iphone_enable_logs(); #endif /// /// Registers the native log handler in Linphone. /// public static void setNativeLogHandler() { #if ANDROID setAndroidLogHandler(); #elif __IOS__ linphone_iphone_enable_logs(); #endif } } /// /// All methods that returns a LinphoneStatus with a value != 0 as an error code in C are translated in C# by throwing a LinphoneException /// #if WINDOWS_UWP public class LinphoneException : System.Exception { public LinphoneException() : base() { } public LinphoneException(string message) : base(message) { } public LinphoneException(string message, System.Exception inner) : base(message, inner) { } } #else [Serializable()] public class LinphoneException : System.Exception { public LinphoneException() : base() { } public LinphoneException(string message) : base(message) { } public LinphoneException(string message, System.Exception inner) : base(message, inner) { } protected LinphoneException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } } #endif [StructLayout(LayoutKind.Sequential)] /// /// Parent class for a Linphone public objects /// public class LinphoneObject { internal IntPtr nativePtr; internal GCHandle handle; [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate void OnLinphoneObjectDataDestroyed(IntPtr data); [DllImport(LinphoneWrapper.BELLE_SIP_LIB_NAME)] #if WINDOWS_UWP static extern int belle_sip_object_data_set(IntPtr ptr, string name, IntPtr data, IntPtr cb); #else static extern int belle_sip_object_data_set(IntPtr ptr, string name, IntPtr data, OnLinphoneObjectDataDestroyed cb); #endif [DllImport(LinphoneWrapper.BELLE_SIP_LIB_NAME)] static extern IntPtr belle_sip_object_data_get(IntPtr ptr, string name); [DllImport(LinphoneWrapper.BELLE_SIP_LIB_NAME)] static extern IntPtr belle_sip_object_ref(IntPtr ptr); [DllImport(LinphoneWrapper.BELLE_SIP_LIB_NAME)] static extern void belle_sip_object_unref(IntPtr ptr); [DllImport(LinphoneWrapper.BELLE_SIP_LIB_NAME)] static extern void belle_sip_object_data_remove(IntPtr ptr, string data); [DllImport(LinphoneWrapper.BCTOOLBOX_LIB_NAME)] static extern IntPtr bctbx_list_next(IntPtr ptr); [DllImport(LinphoneWrapper.BCTOOLBOX_LIB_NAME)] static extern IntPtr bctbx_list_get_data(IntPtr ptr); [DllImport(LinphoneWrapper.BCTOOLBOX_LIB_NAME)] static extern IntPtr bctbx_list_append(IntPtr elem, string data); [DllImport(LinphoneWrapper.BCTOOLBOX_LIB_NAME)] static extern IntPtr bctbx_list_append(IntPtr elem, IntPtr data); #if __IOS__ [MonoPInvokeCallback(typeof(OnLinphoneObjectDataDestroyed))] #endif ~LinphoneObject() { //Console.WriteLine("Destroying " + this.ToString()); if (nativePtr != IntPtr.Zero) { //Console.WriteLine("Unreffing " + this.ToString()); belle_sip_object_data_remove(nativePtr, "cs_obj"); belle_sip_object_unref(nativePtr); handle.Free(); } } internal static T fromNativePtr(IntPtr ptr, bool takeRef=true) where T : LinphoneObject, new() { if (ptr == IntPtr.Zero) return null; IntPtr objPtr = belle_sip_object_data_get(ptr, "cs_obj"); if (objPtr != IntPtr.Zero) { T obj = null; GCHandle handle = GCHandle.FromIntPtr(objPtr); if (handle.IsAllocated) { obj = (T)handle.Target; } if (obj == null) { //Console.WriteLine("Handle target is null " + handle.Target); objPtr = IntPtr.Zero; } else { //Console.WriteLine("Using existing " + obj.ToString()); return obj; } } if (objPtr == IntPtr.Zero) { T obj = new T(); //Console.WriteLine("Creating " + obj.ToString()); if (takeRef) { ptr = belle_sip_object_ref(ptr); //Console.WriteLine("Reffing " + obj.ToString()); } obj.nativePtr = ptr; obj.handle = GCHandle.Alloc(obj, GCHandleType.WeakTrackResurrection); objPtr = GCHandle.ToIntPtr(obj.handle); belle_sip_object_data_set(ptr, "cs_obj", objPtr, IntPtr.Zero); return obj; } return null; } internal static IEnumerable MarshalStringArray(IntPtr arrayPtr) { if (arrayPtr != IntPtr.Zero) { IntPtr ptr = Marshal.ReadIntPtr(arrayPtr); while (ptr != IntPtr.Zero) { string key = Marshal.PtrToStringAnsi(ptr); yield return key; arrayPtr = new IntPtr(arrayPtr.ToInt64() + IntPtr.Size); ptr = Marshal.ReadIntPtr(arrayPtr); } } } internal static IEnumerable MarshalBctbxList(IntPtr listPtr) where T : LinphoneObject, new() { if (listPtr != IntPtr.Zero) { IntPtr ptr = listPtr; while (ptr != IntPtr.Zero) { IntPtr dataPtr = bctbx_list_get_data(ptr); if (dataPtr == IntPtr.Zero) { break; } T obj = fromNativePtr(dataPtr); yield return obj; ptr = bctbx_list_next(ptr); } } } internal static IntPtr StringArrayToBctbxList(IEnumerable stringlist) { IntPtr bctbx_list = IntPtr.Zero; foreach (string s in stringlist) { bctbx_list = bctbx_list_append(bctbx_list, s); } return bctbx_list; } internal static IntPtr ObjectArrayToBctbxList(IEnumerable objlist) where T : LinphoneObject, new() { IntPtr bctbx_list = IntPtr.Zero; foreach (T ptr in objlist) { bctbx_list = bctbx_list_append(bctbx_list, ptr.nativePtr); } return bctbx_list; } } #if ANDROID /// /// Methods that are only found in Android version of Linphone libraries and related to JNI /// public class LinphoneAndroid { [DllImport(LinphoneWrapper.LIB_NAME)] static extern void ms_set_jvm_from_env(IntPtr jnienv); [DllImport(LinphoneWrapper.LIB_NAME)] static extern void setMediastreamerAndroidContext(IntPtr jnienv, IntPtr context); /// /// Sets the JVM and JNI pointers in Linphone, required to be able to make JAVA upcalls. /// Calling this method is mandatory and must be done as soon as possible ! /// public static void setAndroidContext(IntPtr jnienv, IntPtr context) { ms_set_jvm_from_env(jnienv); setMediastreamerAndroidContext(jnienv, context); } } #endif #endregion #region Enums {{#enums}} {{#enum}} {{#doc}} {{#lines}} /// {{{line}}} {{/lines}} {{/doc}} public enum {{enumName}} { {{#values}} {{#doc}} {{#lines}} /// {{{line}}} {{/lines}} {{/doc}} {{name}} = {{value}}, {{/values}} } {{/enum}} {{/enums}} #endregion #region Listeners {{#interfaces}} {{#interface}} [StructLayout(LayoutKind.Sequential)] public class {{interfaceName}} : LinphoneObject { {{#methods}} [DllImport(LinphoneWrapper.LIB_NAME)] {{#cb_setter}} #if WINDOWS_UWP static extern void {{name}}(IntPtr thiz, IntPtr cb); #else static extern void {{name}}(IntPtr thiz, {{name_private}} cb); #endif {{/cb_setter}} {{#delegate}} [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate void {{name_private}}({{params_private}}); public delegate void {{name_public}}({{params_public}}); private {{name_private}} {{var_private}}; private {{name_public}} {{var_public}}; #if __IOS__ [MonoPInvokeCallback(typeof({{name_private}}))] #endif private static void {{cb_name}}({{params_private}}) { {{interfaceClassName}} thiz = fromNativePtr<{{interfaceClassName}}>({{first_param}}); {{#isSimpleListener}}{{interfaceName}} listener = thiz.Listener;{{/isSimpleListener}} {{#isMultiListener}}{{interfaceName}} listener = thiz.CurrentCallbacks;{{/isMultiListener}} listener.{{var_public}}?.Invoke({{{params}}}); } public {{name_public}} {{name}} { get { return {{var_public}}; } set { {{var_public}} = value; #if WINDOWS_UWP {{var_private}} = {{cb_name}}; IntPtr cb = Marshal.GetFunctionPointerForDelegate({{var_private}}); {{c_name_setter}}(nativePtr, cb); #else {{c_name_setter}}(nativePtr, {{cb_name}}); #endif } } {{/delegate}} {{/methods}} } {{/interface}} {{/interfaces}} #endregion #region Classes {{#classes}} {{#_class}} {{#doc}} {{#lines}} /// {{{line}}} {{/lines}} {{/doc}} [StructLayout(LayoutKind.Sequential)] public class {{className}} : LinphoneObject { {{#isLinphoneFactory}} [DllImport(LinphoneWrapper.LIB_NAME)] static extern IntPtr linphone_factory_create_core_cbs(IntPtr factory); public CoreListener CreateCoreListener() { IntPtr coreCbsPtr = linphone_factory_create_core_cbs(nativePtr); return fromNativePtr(coreCbsPtr, false); } {{/isLinphoneFactory}} {{#isLinphoneCall}} [DllImport(LinphoneWrapper.LIB_NAME)] static extern IntPtr linphone_call_get_native_video_window_id(IntPtr thiz); [DllImport(LinphoneWrapper.LIB_NAME)] static extern void linphone_call_set_native_video_window_id(IntPtr thiz, IntPtr id); /// Get the native window handle of the video window, casted as an unsigned long. public string NativeVideoWindowId { get { return Marshal.PtrToStringUni(linphone_call_get_native_video_window_id(nativePtr)); } set { linphone_call_set_native_video_window_id(nativePtr, Marshal.StringToHGlobalUni(value)); } } {{/isLinphoneCall}} {{#isLinphoneCore}} [DllImport(LinphoneWrapper.LIB_NAME)] static extern IntPtr linphone_core_get_native_video_window_id(IntPtr thiz); [DllImport(LinphoneWrapper.LIB_NAME)] static extern void linphone_core_set_native_video_window_id(IntPtr thiz, IntPtr id); /// Get the native window handle of the video window. public string NativeVideoWindowId { get { return Marshal.PtrToStringUni(linphone_core_get_native_video_window_id(nativePtr)); } set { linphone_core_set_native_video_window_id(nativePtr, Marshal.StringToHGlobalUni(value)); } } [DllImport(LinphoneWrapper.LIB_NAME)] static extern IntPtr linphone_core_get_native_preview_window_id(IntPtr thiz); [DllImport(LinphoneWrapper.LIB_NAME)] static extern void linphone_core_set_native_preview_window_id(IntPtr thiz, IntPtr id); /// Get the native window handle of the video preview window. public string NativePreviewWindowId { get { return Marshal.PtrToStringUni(linphone_core_get_native_preview_window_id(nativePtr)); } set { linphone_core_set_native_preview_window_id(nativePtr, Marshal.StringToHGlobalUni(value)); } } {{/isLinphoneCore}} {{#dllImports}} [DllImport(LinphoneWrapper.LIB_NAME)] {{{prototype}}} {{#has_second_prototype}} [DllImport(LinphoneWrapper.LIB_NAME)] {{second_prototype}} {{/has_second_prototype}} {{#has_property}} {{#doc}} {{#lines}} /// {{{line}}} {{/lines}} {{/doc}} {{property_static}}public {{{property_return}}} {{property_name}} { {{#has_getter}} get { {{#is_string}} IntPtr stringPtr = {{getter_c_name}}({{getter_nativePtr}}); return Marshal.PtrToStringAnsi(stringPtr); {{/is_string}} {{#is_bool}} return {{getter_c_name}}({{getter_nativePtr}}) != 0; {{/is_bool}} {{#is_class}} IntPtr ptr = {{getter_c_name}}({{getter_nativePtr}}); return fromNativePtr<{{return}}>(ptr, {{takeRef}}); {{/is_class}} {{#is_enum}} return {{getter_c_name}}({{getter_nativePtr}}); {{/is_enum}} {{#is_generic}} return {{getter_c_name}}({{getter_nativePtr}}); {{/is_generic}} {{#is_string_list}} return MarshalStringArray({{getter_c_name}}({{getter_nativePtr}})); {{/is_string_list}} {{#is_class_list}} return MarshalBctbxList<{{{list_type}}}>({{getter_c_name}}({{getter_nativePtr}})); {{/is_class_list}} } {{/has_getter}} {{#has_setter}} set { {{#is_string}} {{#exception}}int exception_result = {{/exception}}{{setter_c_name}}({{setter_nativePtr}}value); {{#exception}}if (exception_result != 0) throw new LinphoneException("{{property_name}} setter returned value " + exception_result);{{/exception}} {{/is_string}} {{#is_bool}} {{#exception}}int exception_result = {{/exception}}{{setter_c_name}}({{setter_nativePtr}}value ? (char)1 : (char)0); {{#exception}}if (exception_result != 0) throw new LinphoneException("{{property_name}} setter returned value " + exception_result);{{/exception}} {{/is_bool}} {{#is_class}} {{#exception}}int exception_result = {{/exception}}{{setter_c_name}}({{setter_nativePtr}}value.nativePtr); {{#exception}}if (exception_result != 0) throw new LinphoneException("{{property_name}} setter returned value " + exception_result);{{/exception}} {{/is_class}} {{#is_enum}} {{#exception}}int exception_result = {{/exception}}{{setter_c_name}}({{setter_nativePtr}}(int)value); {{#exception}}if (exception_result != 0) throw new LinphoneException("{{property_name}} setter returned value " + exception_result);{{/exception}} {{/is_enum}} {{#is_generic}} {{#exception}}int exception_result = {{/exception}}{{setter_c_name}}({{setter_nativePtr}}value); {{#exception}}if (exception_result != 0) throw new LinphoneException("{{property_name}} setter returned value " + exception_result);{{/exception}} {{/is_generic}} {{#is_string_list}} {{#exception}}int exception_result = {{/exception}}{{setter_c_name}}({{setter_nativePtr}}StringArrayToBctbxList(value)); {{#exception}}if (exception_result != 0) throw new LinphoneException("{{property_name}} setter returned value " + exception_result);{{/exception}} {{/is_string_list}} {{#is_class_list}} {{#exception}}int exception_result = {{/exception}}{{setter_c_name}}({{setter_nativePtr}}ObjectArrayToBctbxList<{{{list_type}}}>(value)); {{#exception}}if (exception_result != 0) throw new LinphoneException("{{property_name}} setter returned value " + exception_result);{{/exception}} {{/is_class_list}} } {{/has_setter}} } {{/has_property}} {{#has_impl}} {{#impl}} {{#doc}} {{#lines}} /// {{{line}}} {{/lines}} {{/doc}} public {{static}}{{override}}{{{type}}} {{name}}({{{args}}}) { {{#is_string}} IntPtr stringPtr = {{c_name}}({{nativePtr}}{{c_args}}); return Marshal.PtrToStringAnsi(stringPtr); {{/is_string}} {{#is_bool}} {{return}}{{c_name}}({{nativePtr}}{{c_args}}) == (char)0 ? false : true; {{/is_bool}} {{#is_class}} IntPtr ptr = {{c_name}}({{nativePtr}}{{c_args}}); return fromNativePtr<{{type}}>(ptr, {{takeRef}}); {{/is_class}} {{#is_enum}} {{#exception}}int exception_result = {{/exception}}{{return}}{{c_name}}({{nativePtr}}{{{c_args}}}); {{#exception}}if (exception_result != 0) throw new LinphoneException("{{name}} returned value " + exception_result);{{/exception}} {{/is_enum}} {{#is_generic}} {{#exception}}int exception_result = {{/exception}}{{return}}{{c_name}}({{nativePtr}}{{{c_args}}}); {{#exception}}if (exception_result != 0) throw new LinphoneException("{{name}} returned value" + exception_result);{{/exception}} {{/is_generic}} {{#is_string_list}} return MarshalStringArray({{c_name}}({{nativePtr}}{{c_args}})); {{/is_string_list}} {{#is_class_list}} return MarshalBctbxList<{{{list_type}}}>({{c_name}}({{nativePtr}}{{c_args}})); {{/is_class_list}} } {{/impl}} {{/has_impl}} {{/dllImports}} } {{/_class}} {{/classes}} #endregion }