From aa12bbd2550b13534175638634b9fda117bca99e Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Sun, 22 Mar 2015 08:53:03 +0000 Subject: Re #1790: - Removed android_opengl_get_surface() for renderer view setup, Java application can directly supply Surface object as renderer window. - Added renderer view on pjsua CLI app sample. git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@5014 74dad513-b988-da41-8d7b-12977e46ad98 --- pjmedia/src/pjmedia-videodev/android_opengl.c | 12 - pjsip-apps/src/pjsua/android/jni/pjsua.i | 15 +- .../src/pjsua/android/jni/pjsua_app_callback.cpp | 41 +++ .../src/pjsua/android/jni/pjsua_app_callback.h | 3 + .../src/pjsua/android/res/layout/activity_main.xml | 34 +- .../android/src/org/pjsip/pjsua/MainActivity.java | 365 +++++++++++---------- .../src/org/pjsip/pjsua2/app/CallActivity.java | 2 +- .../android/src/org/pjsip/pjsua2/app/MyApp.java | 9 + pjsip-apps/src/swig/pjsua2.i | 16 +- 9 files changed, 294 insertions(+), 203 deletions(-) diff --git a/pjmedia/src/pjmedia-videodev/android_opengl.c b/pjmedia/src/pjmedia-videodev/android_opengl.c index 97493452..292f1497 100644 --- a/pjmedia/src/pjmedia-videodev/android_opengl.c +++ b/pjmedia/src/pjmedia-videodev/android_opengl.c @@ -136,18 +136,6 @@ static pjmedia_vid_dev_stream_op stream_op = &andgl_stream_destroy }; -static void get_jvm(JNIEnv **jni_env) -{ - (*pj_jni_jvm)->GetEnv(pj_jni_jvm, (void **)jni_env, JNI_VERSION_1_4); -} - -void* android_opengl_get_surface(jobject surface) -{ - JNIEnv *env = 0; - get_jvm(&env); - return ((env && surface)? ANativeWindow_fromSurface(env, surface): NULL); -} - int pjmedia_vid_dev_opengl_imp_get_cap(void) { return PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW | diff --git a/pjsip-apps/src/pjsua/android/jni/pjsua.i b/pjsip-apps/src/pjsua/android/jni/pjsua.i index 99443160..d54bfc3c 100755 --- a/pjsip-apps/src/pjsua/android/jni/pjsua.i +++ b/pjsip-apps/src/pjsua/android/jni/pjsua.i @@ -3,6 +3,7 @@ %{ #include "pjsua_app_callback.h" #include "../../pjsua_app.h" +#include #ifdef __cplusplus extern "C" { @@ -16,14 +17,12 @@ extern "C" { #endif %} -int pjsuaStart(); -void pjsuaDestroy(); -int pjsuaRestart(); - -/* turn on director wrapping PjsuaAppCallback */ +/* Turn on director wrapping PjsuaAppCallback */ %feature("director") PjsuaAppCallback; -%include "pjsua_app_callback.h" - -void setCallbackObject(PjsuaAppCallback* callback); +/* Convert Surface object to ANativeWindow for setIncomingVideoRenderer() */ +%typemap(in) jobject surface { + $1 = (jobject)ANativeWindow_fromSurface(jenv, $input); +} +%include "pjsua_app_callback.h" diff --git a/pjsip-apps/src/pjsua/android/jni/pjsua_app_callback.cpp b/pjsip-apps/src/pjsua/android/jni/pjsua_app_callback.cpp index f9b0dcee..fe8daf09 100644 --- a/pjsip-apps/src/pjsua/android/jni/pjsua_app_callback.cpp +++ b/pjsip-apps/src/pjsua/android/jni/pjsua_app_callback.cpp @@ -29,6 +29,8 @@ static PjsuaAppCallback* registeredCallbackObject = NULL; static pjsua_app_cfg_t android_app_config; static int restart_argc; static char **restart_argv; +static pjsua_callback pjsua_cb_orig; +static jobject callVideoSurface; extern const char *pjsua_app_def_argv[]; @@ -41,11 +43,44 @@ static void log_writer(int level, const char *data, int len) } } +static void on_call_media_state(pjsua_call_id call_id) +{ + pjsua_call_info call_info; + unsigned mi; + pj_bool_t has_error = PJ_FALSE; + + pjsua_call_get_info(call_id, &call_info); + + for (mi=0; mitype == PJMEDIA_TYPE_VIDEO && + med_info->status == PJSUA_CALL_MEDIA_ACTIVE && + med_info->stream.vid.win_in != PJSUA_INVALID_ID) + { + pjmedia_vid_dev_hwnd vhwnd; + + /* Setup renderer surface */ + pj_bzero(&vhwnd, sizeof(vhwnd)); + vhwnd.type = PJMEDIA_VID_DEV_HWND_TYPE_ANDROID; + vhwnd.info.window = callVideoSurface; + pjsua_vid_win_set_win(med_info->stream.vid.win_in, &vhwnd); + break; + } + } + + /* Forward to original callback */ + if (pjsua_cb_orig.on_call_media_state) + (*pjsua_cb_orig.on_call_media_state)(call_id); +} /** Callback wrapper **/ static void on_cli_config(pjsua_app_config *cfg) { + pjsua_cb_orig = cfg->cfg.cb; cfg->log_cfg.cb = &log_writer; + + /* Override pjsua callback, e.g: to install renderer view */ + cfg->cfg.cb.on_call_media_state = &on_call_media_state; } static void on_cli_started(pj_status_t status, const char *msg) @@ -126,4 +161,10 @@ void setCallbackObject(PjsuaAppCallback* callback) registeredCallbackObject = callback; } + +void setIncomingVideoRenderer(jobject surface) +{ + callVideoSurface = surface; +} + #endif diff --git a/pjsip-apps/src/pjsua/android/jni/pjsua_app_callback.h b/pjsip-apps/src/pjsua/android/jni/pjsua_app_callback.h index 561b5ce3..38dc996d 100644 --- a/pjsip-apps/src/pjsua/android/jni/pjsua_app_callback.h +++ b/pjsip-apps/src/pjsua/android/jni/pjsua_app_callback.h @@ -19,6 +19,8 @@ #ifndef __PJSUA_APP_CALLBACK_H__ #define __PJSUA_APP_CALLBACK_H__ +#include + class PjsuaAppCallback { public: virtual ~PjsuaAppCallback() {} @@ -31,6 +33,7 @@ int pjsuaStart(); void pjsuaDestroy(); int pjsuaRestart(); void setCallbackObject(PjsuaAppCallback* callback); +void setIncomingVideoRenderer(jobject surface); } #endif /* __PJSUA_APP_CALLBACK_H__ */ diff --git a/pjsip-apps/src/pjsua/android/res/layout/activity_main.xml b/pjsip-apps/src/pjsua/android/res/layout/activity_main.xml index da258b76..24a13372 100644 --- a/pjsip-apps/src/pjsua/android/res/layout/activity_main.xml +++ b/pjsip-apps/src/pjsua/android/res/layout/activity_main.xml @@ -4,31 +4,43 @@ android:layout_height="fill_parent" android:orientation="vertical" > - + android:textIsSelectable="false" /> + + - + android:layout_above="@+id/textApp" + android:layout_centerHorizontal="true" + android:contentDescription="@string/app_name" + android:src="@drawable/main_image" /> \ No newline at end of file diff --git a/pjsip-apps/src/pjsua/android/src/org/pjsip/pjsua/MainActivity.java b/pjsip-apps/src/pjsua/android/src/org/pjsip/pjsua/MainActivity.java index aa0f9876..927a70b6 100644 --- a/pjsip-apps/src/pjsua/android/src/org/pjsip/pjsua/MainActivity.java +++ b/pjsip-apps/src/pjsua/android/src/org/pjsip/pjsua/MainActivity.java @@ -25,218 +25,253 @@ import android.app.Activity; import android.content.pm.ApplicationInfo; import android.os.Bundle; import android.util.Log; +import android.view.SurfaceHolder; +import android.view.SurfaceView; import android.os.Handler; import android.os.Message; import android.widget.TextView; class CONST { - public static final String LIB_FILENAME = "pjsua"; - public static final String TAG = "pjsua"; - public static final Boolean AUTOKILL_ON_FINISH = true; - public enum MSG_TYPE { - STR_DEBUG, - STR_INFO, - STR_ERROR, - CLI_STOP, - CLI_RESTART, - QUIT - }; + public static final String LIB_FILENAME = "pjsua"; + public static final String TAG = "pjsua"; + public static final Boolean AUTOKILL_ON_FINISH = true; + public enum MSG_TYPE { + STR_DEBUG, + STR_INFO, + STR_ERROR, + CLI_STOP, + CLI_RESTART, + QUIT + }; } class LOG { - public static void DEBUG(Handler h, String str) { - Message msg = Message.obtain(h, CONST.MSG_TYPE.STR_DEBUG.ordinal(), - str); - msg.sendToTarget(); - } - public static void INFO(Handler h, String str) { - Message msg = Message.obtain(h, CONST.MSG_TYPE.STR_INFO.ordinal(), - str); - msg.sendToTarget(); - } - public static void ERROR(Handler h, String str) { - Message msg = Message.obtain(h, CONST.MSG_TYPE.STR_ERROR.ordinal(), - str); - msg.sendToTarget(); - } + public static void DEBUG(Handler h, String str) { + Message msg = Message.obtain(h, CONST.MSG_TYPE.STR_DEBUG.ordinal(), + str); + msg.sendToTarget(); + } + public static void INFO(Handler h, String str) { + Message msg = Message.obtain(h, CONST.MSG_TYPE.STR_INFO.ordinal(), + str); + msg.sendToTarget(); + } + public static void ERROR(Handler h, String str) { + Message msg = Message.obtain(h, CONST.MSG_TYPE.STR_ERROR.ordinal(), + str); + msg.sendToTarget(); + } } -public class MainActivity extends Activity { - private MyHandler ui_handler = new MyHandler(this); - private static MyCallback callback; - - private static class MyHandler extends Handler { - private final WeakReference mTarget; - - public MyHandler(MainActivity target) { - mTarget = new WeakReference(target); - } - - @Override - public void handleMessage(Message m) { - MainActivity target = mTarget.get(); - if (target == null) - return; - - if (m.what == CONST.MSG_TYPE.STR_DEBUG.ordinal()) { - Log.d(CONST.TAG, (String)m.obj); - } else if (m.what == CONST.MSG_TYPE.STR_INFO.ordinal()) { - target.updateStatus((String)m.obj); - Log.i(CONST.TAG, (String)m.obj); - } else if (m.what == CONST.MSG_TYPE.STR_ERROR.ordinal()) { - target.updateStatus((String)m.obj); - Log.e(CONST.TAG, (String)m.obj); - } else if (m.what == CONST.MSG_TYPE.CLI_STOP.ordinal()) { - pjsua.pjsuaDestroy(); - LOG.INFO(this, "Telnet Unavailable"); - } else if (m.what == CONST.MSG_TYPE.CLI_RESTART.ordinal()) { - int status = pjsua.pjsuaRestart(); - if (status != 0) { - LOG.INFO(this, "Failed restarting telnet"); - } - } else if (m.what == CONST.MSG_TYPE.QUIT.ordinal()) { - target.finish(); - System.gc(); - android.os.Process.killProcess(android.os.Process.myPid()); - } +public class MainActivity extends Activity implements SurfaceHolder.Callback { + private MyHandler ui_handler = new MyHandler(this); + private static MyCallback callback; + + private static class MyHandler extends Handler { + private final WeakReference mTarget; + + public MyHandler(MainActivity target) { + mTarget = new WeakReference(target); + } + + @Override + public void handleMessage(Message m) { + MainActivity target = mTarget.get(); + if (target == null) + return; + + if (m.what == CONST.MSG_TYPE.STR_DEBUG.ordinal()) { + Log.d(CONST.TAG, (String)m.obj); + } else if (m.what == CONST.MSG_TYPE.STR_INFO.ordinal()) { + target.updateStatus((String)m.obj); + Log.i(CONST.TAG, (String)m.obj); + } else if (m.what == CONST.MSG_TYPE.STR_ERROR.ordinal()) { + target.updateStatus((String)m.obj); + Log.e(CONST.TAG, (String)m.obj); + } else if (m.what == CONST.MSG_TYPE.CLI_STOP.ordinal()) { + pjsua.pjsuaDestroy(); + LOG.INFO(this, "Telnet Unavailable"); + } else if (m.what == CONST.MSG_TYPE.CLI_RESTART.ordinal()) { + int status = pjsua.pjsuaRestart(); + if (status != 0) { + LOG.INFO(this, "Failed restarting telnet"); } + } else if (m.what == CONST.MSG_TYPE.QUIT.ordinal()) { + target.finish(); + System.gc(); + android.os.Process.killProcess(android.os.Process.myPid()); + } + } } - - /** Callback object **/ - private static class MyCallback extends PjsuaAppCallback { - private WeakReference ui_handler; - - public MyCallback(Handler in_ui_handler) { - set_ui_handler(in_ui_handler); - } - - public void set_ui_handler(Handler in_ui_handler) { - ui_handler = new WeakReference(in_ui_handler); - } - - @Override - public void onStarted(String msg) { - Handler ui = ui_handler.get(); - LOG.INFO(ui, msg); - } - - @Override - public void onStopped(int restart) { - Handler ui = ui_handler.get(); - /** Use timer to stopped/restart **/ - if (restart != 0) { - LOG.INFO(ui, "Telnet Restarting"); - Message msg = Message.obtain(ui, - CONST.MSG_TYPE.CLI_RESTART.ordinal()); - ui.sendMessageDelayed(msg, 100); - } else { - LOG.INFO(ui, "Telnet Stopping"); - Message msg = Message.obtain(ui, - CONST.MSG_TYPE.CLI_STOP.ordinal()); - ui.sendMessageDelayed(msg, 100); - } - } + + /** Callback object **/ + private static class MyCallback extends PjsuaAppCallback { + private WeakReference ui_handler; + + public MyCallback(Handler in_ui_handler) { + set_ui_handler(in_ui_handler); } - - private void updateStatus(String output) { - TextView tStatus = (TextView) findViewById(R.id.textStatus); - tStatus.setText(output); + + public void set_ui_handler(Handler in_ui_handler) { + ui_handler = new WeakReference(in_ui_handler); + } + + @Override + public void onStarted(String msg) { + Handler ui = ui_handler.get(); + LOG.INFO(ui, msg); } - + + @Override + public void onStopped(int restart) { + Handler ui = ui_handler.get(); + /** Use timer to stopped/restart **/ + if (restart != 0) { + LOG.INFO(ui, "Telnet Restarting"); + Message msg = Message.obtain(ui, + CONST.MSG_TYPE.CLI_RESTART.ordinal()); + ui.sendMessageDelayed(msg, 100); + } else { + LOG.INFO(ui, "Telnet Stopping"); + Message msg = Message.obtain(ui, + CONST.MSG_TYPE.CLI_STOP.ordinal()); + ui.sendMessageDelayed(msg, 100); + } + } + } + + private void updateStatus(String output) { + TextView tStatus = (TextView) findViewById(R.id.textStatus); + tStatus.setText(output); + } + @Override protected void onCreate(Bundle savedInstanceState) { - LOG.DEBUG(ui_handler, "=== Activity::onCreate() ==="); - super.onCreate(savedInstanceState); - - init_view(); + LOG.DEBUG(ui_handler, "=== Activity::onCreate() ==="); + super.onCreate(savedInstanceState); - init_lib(); - } + init_view(); + + init_lib(); + } @Override protected void onStart() { - LOG.DEBUG(ui_handler, "=== Activity::onStart() ==="); - super.onStart(); + LOG.DEBUG(ui_handler, "=== Activity::onStart() ==="); + super.onStart(); } - + @Override protected void onRestart() { - LOG.DEBUG(ui_handler, "=== Activity::onRestart() ==="); - super.onRestart(); + LOG.DEBUG(ui_handler, "=== Activity::onRestart() ==="); + super.onRestart(); } @Override protected void onResume() { - LOG.DEBUG(ui_handler, "=== Activity::onResume() ==="); - super.onResume(); + LOG.DEBUG(ui_handler, "=== Activity::onResume() ==="); + super.onResume(); } @Override protected void onPause() { - LOG.DEBUG(ui_handler, "=== Activity::onPause() ==="); - super.onPause(); + LOG.DEBUG(ui_handler, "=== Activity::onPause() ==="); + super.onPause(); } @Override protected void onStop() { - LOG.DEBUG(ui_handler, "=== Activity::onStop() ==="); - super.onStop(); + LOG.DEBUG(ui_handler, "=== Activity::onStop() ==="); + super.onStop(); } @Override protected void onDestroy() { - LOG.DEBUG(ui_handler, "=== Activity::onDestroy() ==="); - super.onDestroy(); + LOG.DEBUG(ui_handler, "=== Activity::onDestroy() ==="); + super.onDestroy(); } - + @Override protected void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); + super.onSaveInstanceState(outState); } @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { - super.onRestoreInstanceState(savedInstanceState); + super.onRestoreInstanceState(savedInstanceState); } - - private void init_view() { - setContentView(R.layout.activity_main); + + private void init_view() { + setContentView(R.layout.activity_main); + } + + private int init_lib() { + LOG.INFO(ui_handler, "Loading module..."); + + // Try loading video dependency libs + try { + System.loadLibrary("openh264"); + System.loadLibrary("yuv"); + } catch (UnsatisfiedLinkError e) { + LOG.ERROR(ui_handler, "UnsatisfiedLinkError: " + e.getMessage()); + LOG.ERROR(ui_handler, "This could be safely ignored if you "+ + "don't need video."); } - - private int init_lib() { - LOG.INFO(ui_handler, "Loading module..."); - try { - System.loadLibrary(CONST.LIB_FILENAME); - } catch (UnsatisfiedLinkError e) { - LOG.ERROR(ui_handler, "UnsatisfiedLinkError: " + e.getMessage()); - return -1; - } - - // Wait for GDB to init - if ((getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) - { - try { - Thread.sleep(5000); - } catch (InterruptedException e) { - LOG.ERROR(ui_handler, "InterruptedException: " + - e.getMessage()); - } - } - - // Set callback object - if (callback == null) - callback = new MyCallback(ui_handler); - - pjsua.setCallbackObject(callback); - - LOG.INFO(ui_handler, "Starting module.."); - - int rc = pjsua.pjsuaStart(); - - if (rc != 0) { - LOG.INFO(ui_handler, "Failed starting telnet"); - } - - return 0; + + // Load pjsua + try { + System.loadLibrary(CONST.LIB_FILENAME); + } catch (UnsatisfiedLinkError e) { + LOG.ERROR(ui_handler, "UnsatisfiedLinkError: " + e.getMessage()); + return -1; } + + // Wait for GDB to init, for native debugging only + if (false && (getApplicationInfo().flags & + ApplicationInfo.FLAG_DEBUGGABLE) != 0) + { + try { + Thread.sleep(5000); + } catch (InterruptedException e) { + LOG.ERROR(ui_handler, "InterruptedException: " + + e.getMessage()); + } + } + + // Set callback object + if (callback == null) + callback = new MyCallback(ui_handler); + + pjsua.setCallbackObject(callback); + + SurfaceView surfaceView = (SurfaceView) + findViewById(R.id.surfaceViewIncomingCall); + surfaceView.getHolder().addCallback(this); + + LOG.INFO(ui_handler, "Starting module.."); + + int rc = pjsua.pjsuaStart(); + + if (rc != 0) { + LOG.INFO(ui_handler, "Failed starting telnet"); + } + + return 0; + } + + public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) + { + pjsua.setIncomingVideoRenderer(holder.getSurface()); + } + + public void surfaceCreated(SurfaceHolder holder) + { + pjsua.setIncomingVideoRenderer(holder.getSurface()); + } + + public void surfaceDestroyed(SurfaceHolder holder) + { + pjsua.setIncomingVideoRenderer(null); + } + } diff --git a/pjsip-apps/src/swig/java/android/src/org/pjsip/pjsua2/app/CallActivity.java b/pjsip-apps/src/swig/java/android/src/org/pjsip/pjsua2/app/CallActivity.java index 9a057778..ff81b026 100644 --- a/pjsip-apps/src/swig/java/android/src/org/pjsip/pjsua2/app/CallActivity.java +++ b/pjsip-apps/src/swig/java/android/src/org/pjsip/pjsua2/app/CallActivity.java @@ -79,7 +79,7 @@ public class CallActivity extends Activity if (holder == null) vidWH.getHandle().setWindow(null); else - vidWH.getHandle().setWindow(pjsua2.android_opengl_get_surface(holder.getSurface())); + vidWH.getHandle().setWindow(holder.getSurface()); try { MainActivity.currentCall.vidWin.setWindow(vidWH); } catch (Exception e) {} diff --git a/pjsip-apps/src/swig/java/android/src/org/pjsip/pjsua2/app/MyApp.java b/pjsip-apps/src/swig/java/android/src/org/pjsip/pjsua2/app/MyApp.java index 92cc66bb..fbc3cedf 100644 --- a/pjsip-apps/src/swig/java/android/src/org/pjsip/pjsua2/app/MyApp.java +++ b/pjsip-apps/src/swig/java/android/src/org/pjsip/pjsua2/app/MyApp.java @@ -20,6 +20,7 @@ package org.pjsip.pjsua2.app; import java.io.File; import java.util.ArrayList; + import org.pjsip.pjsua2.*; @@ -243,6 +244,14 @@ class MyAccountConfig { class MyApp { static { + try{ + System.loadLibrary("openh264"); + System.loadLibrary("yuv"); + } catch (UnsatisfiedLinkError e) { + System.out.println("UnsatisfiedLinkError: " + e.getMessage()); + System.out.println("This could be safely ignored if you " + + "don't need video."); + } System.loadLibrary("pjsua2"); System.out.println("Library loaded"); } diff --git a/pjsip-apps/src/swig/pjsua2.i b/pjsip-apps/src/swig/pjsua2.i index 91f91916..4e58e21e 100644 --- a/pjsip-apps/src/swig/pjsua2.i +++ b/pjsip-apps/src/swig/pjsua2.i @@ -101,16 +101,20 @@ using namespace pj; %template(AudioDevInfoVector) std::vector; %template(CodecInfoVector) std::vector; -%include "pjsua2/media.hpp" -// Create an interface for android_opengl_get_surface() +/* pj::WindowHandle::setWindow() receives Surface object */ #if defined(SWIGJAVA) && defined(__ANDROID__) -%inline %{ -extern "C" { - void* android_opengl_get_surface(jobject surface); +%{#include %} +%ignore pj::WindowHandle::display; +%ignore pj::WindowHandle::window; +%typemap(in) jobject surface { + $1 = (jobject)ANativeWindow_fromSurface(jenv, $input); +} +%extend pj::WindowHandle { + void setWindow(jobject surface) { $self->window = surface; } } -%} #endif +%include "pjsua2/media.hpp" %include "pjsua2/presence.hpp" %include "pjsua2/account.hpp" %include "pjsua2/call.hpp" -- cgit v1.2.3