diff options
6 files changed, 350 insertions, 36 deletions
diff --git a/pjsip-apps/src/swig/java/android/res/layout/activity_call.xml b/pjsip-apps/src/swig/java/android/res/layout/activity_call.xml index 63d90a02..17acf1c5 100644 --- a/pjsip-apps/src/swig/java/android/res/layout/activity_call.xml +++ b/pjsip-apps/src/swig/java/android/res/layout/activity_call.xml @@ -23,24 +23,51 @@ android:layout_height="wrap_content"
android:gravity="center"
android:text="Call state" />
-
- <Button
- android:id="@+id/buttonAccept"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:onClick="acceptCall"
- android:text="Accept" />
-
- <Button
- android:id="@+id/buttonHangup"
+
+ <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:onClick="hangupCall"
- android:text="Reject" />
-
+ android:orientation="horizontal">
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_weight=".50">
+
+ <Button
+ android:id="@+id/buttonAccept"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:onClick="acceptCall"
+ android:text="Accept" />
+
+ <Button
+ android:id="@+id/buttonHangup"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:onClick="hangupCall"
+ android:text="Reject" />
+
+ <Button
+ android:id="@+id/buttonShowPreview"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:onClick="showPreview"
+ android:text="@+string/show_preview" />
+
+ </LinearLayout>
+ <SurfaceView
+ android:id="@+id/surfacePreviewCapture"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight=".50" />
+
+ </LinearLayout>
+
<SurfaceView
- android:id="@+id/surfaceIncomingVideo"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
-
+ android:id="@+id/surfaceIncomingVideo"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
</LinearLayout>
\ No newline at end of file diff --git a/pjsip-apps/src/swig/java/android/res/values/strings.xml b/pjsip-apps/src/swig/java/android/res/values/strings.xml index 2ee52b69..cce48a16 100644 --- a/pjsip-apps/src/swig/java/android/res/values/strings.xml +++ b/pjsip-apps/src/swig/java/android/res/values/strings.xml @@ -5,5 +5,6 @@ <string name="action_settings">Settings</string>
<string name="title_activity_call">Call</string>
<string name="hello_world">Hello world!</string>
-
+ <string name="show_preview">Show Preview</string>
+ <string name="hide_preview">Hide Preview</string>
</resources>
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 b74b3cd5..91123be7 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 @@ -30,11 +30,66 @@ import android.app.Activity; import org.pjsip.pjsua2.*; +class VideoPreviewHandler implements SurfaceHolder.Callback +{ + public boolean videoPreviewActive = false; + + public void updateVideoPreview(SurfaceHolder holder) + { + if (MainActivity.currentCall != null && + MainActivity.currentCall.vidWin != null && + MainActivity.currentCall.vidPrev != null) + { + if (videoPreviewActive) { + VideoWindowHandle vidWH = new VideoWindowHandle(); + vidWH.getHandle().setWindow(holder.getSurface()); + VideoPreviewOpParam vidPrevParam = new VideoPreviewOpParam(); + vidPrevParam.setWindow(vidWH); + try { + MainActivity.currentCall.vidPrev.start(vidPrevParam); + } catch (Exception e) { + System.out.println(e); + } + } else { + try { + MainActivity.currentCall.vidPrev.stop(); + } catch (Exception e) { + System.out.println(e); + } + } + } + } + + @Override + public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) + { + updateVideoPreview(holder); + } + + @Override + public void surfaceCreated(SurfaceHolder holder) + { + + } + + @Override + public void surfaceDestroyed(SurfaceHolder holder) + { + try { + MainActivity.currentCall.vidPrev.stop(); + } catch (Exception e) { + System.out.println(e); + } + } +} + public class CallActivity extends Activity implements Handler.Callback, SurfaceHolder.Callback { public static Handler handler_; + private static VideoPreviewHandler previewHandler = + new VideoPreviewHandler(); private final Handler handler = new Handler(this); private static CallInfo lastCallInfo; @@ -45,14 +100,22 @@ public class CallActivity extends Activity super.onCreate(savedInstanceState); setContentView(R.layout.activity_call); - SurfaceView surfaceView = (SurfaceView) + SurfaceView surfaceInVideo = (SurfaceView) findViewById(R.id.surfaceIncomingVideo); + SurfaceView surfacePreview = (SurfaceView) + findViewById(R.id.surfacePreviewCapture); + Button buttonShowPreview = (Button) + findViewById(R.id.buttonShowPreview); + if (MainActivity.currentCall == null || MainActivity.currentCall.vidWin == null) { - surfaceView.setVisibility(View.GONE); + surfaceInVideo.setVisibility(View.GONE); + buttonShowPreview.setVisibility(View.GONE); } - surfaceView.getHolder().addCallback(this); + setupVideoPreview(surfacePreview, buttonShowPreview); + surfaceInVideo.getHolder().addCallback(this); + surfacePreview.getHolder().addCallback(previewHandler); handler_ = handler; if (MainActivity.currentCall != null) { @@ -73,26 +136,34 @@ public class CallActivity extends Activity super.onDestroy(); handler_ = null; } - - private void updateVideoWindow(SurfaceHolder holder) - { + + private void updateVideoWindow(boolean show) + { if (MainActivity.currentCall != null && - MainActivity.currentCall.vidWin != null) + MainActivity.currentCall.vidWin != null && + MainActivity.currentCall.vidPrev != null) { - VideoWindowHandle vidWH = new VideoWindowHandle(); - if (holder == null) + SurfaceView surfaceInVideo = (SurfaceView) + findViewById(R.id.surfaceIncomingVideo); + + VideoWindowHandle vidWH = new VideoWindowHandle(); + if (show) { + vidWH.getHandle().setWindow( + surfaceInVideo.getHolder().getSurface()); + } else { vidWH.getHandle().setWindow(null); - else - vidWH.getHandle().setWindow(holder.getSurface()); + } try { MainActivity.currentCall.vidWin.setWindow(vidWH); - } catch (Exception e) {} + } catch (Exception e) { + System.out.println(e); + } } } - + public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { - updateVideoWindow(holder); + updateVideoWindow(true); } public void surfaceCreated(SurfaceHolder holder) @@ -101,7 +172,7 @@ public class CallActivity extends Activity public void surfaceDestroyed(SurfaceHolder holder) { - updateVideoWindow(null); + updateVideoWindow(false); } public void acceptCall(View view) @@ -132,13 +203,45 @@ public class CallActivity extends Activity } } } + + public void setupVideoPreview(SurfaceView surfacePreview, + Button buttonShowPreview) + { + surfacePreview.setVisibility(previewHandler.videoPreviewActive? + View.VISIBLE:View.GONE); + + buttonShowPreview.setText(previewHandler.videoPreviewActive? + getString(R.string.hide_preview): + getString(R.string.show_preview)); + } + + public void showPreview(View view) + { + SurfaceView surfacePreview = (SurfaceView) + findViewById(R.id.surfacePreviewCapture); + + Button buttonShowPreview = (Button) + findViewById(R.id.buttonShowPreview); + + + previewHandler.videoPreviewActive = !previewHandler.videoPreviewActive; + + setupVideoPreview(surfacePreview, buttonShowPreview); + + previewHandler.updateVideoPreview(surfacePreview.getHolder()); + } private void setupVideoSurface() { - SurfaceView surfaceView = (SurfaceView) + SurfaceView surfaceInVideo = (SurfaceView) findViewById(R.id.surfaceIncomingVideo); - surfaceView.setVisibility(View.VISIBLE); - updateVideoWindow(surfaceView.getHolder()); + SurfaceView surfacePreview = (SurfaceView) + findViewById(R.id.surfacePreviewCapture); + Button buttonShowPreview = (Button) + findViewById(R.id.buttonShowPreview); + surfaceInVideo.setVisibility(View.VISIBLE); + buttonShowPreview.setVisibility(View.VISIBLE); + surfacePreview.setVisibility(View.GONE); } @Override 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 846235d5..d4fbc307 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 @@ -49,6 +49,7 @@ class MyLogWriter extends LogWriter class MyCall extends Call { public VideoWindow vidWin; + public VideoPreview vidPrev; MyCall(MyAccount acc, int call_id) { @@ -112,6 +113,7 @@ class MyCall extends Call cmi.getVideoIncomingWindowId() != pjsua2.INVALID_ID) { vidWin = new VideoWindow(cmi.getVideoIncomingWindowId()); + vidPrev = new VideoPreview(cmi.getVideoCapDev()); } } diff --git a/pjsip/include/pjsua2/media.hpp b/pjsip/include/pjsua2/media.hpp index 181001e4..16df4ca5 100644 --- a/pjsip/include/pjsua2/media.hpp +++ b/pjsip/include/pjsua2/media.hpp @@ -1488,6 +1488,105 @@ private: pjsua_vid_win_id winId; }; +/** + * This structure contains parameters for VideoPreview::start() + */ +struct VideoPreviewOpParam { + /** + * Device ID for the video renderer to be used for rendering the + * capture stream for preview. This parameter is ignored if native + * preview is being used. + * + * Default: PJMEDIA_VID_DEFAULT_RENDER_DEV + */ + pjmedia_vid_dev_index rendId; + + /** + * Show window initially. + * + * Default: PJ_TRUE. + */ + bool show; + + /** + * Window flags. The value is a bitmask combination of + * \a pjmedia_vid_dev_wnd_flag. + * + * Default: 0. + */ + unsigned windowFlags; + + /** + * Media format. If left unitialized, this parameter will not be used. + */ + MediaFormat format; + + /** + * Optional output window to be used to display the video preview. + * This parameter will only be used if the video device supports + * PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW capability and the capability + * is not read-only. + */ + VideoWindowHandle window; + +public: + /** + * Default constructor initializes with default values. + */ + VideoPreviewOpParam(); + + /** + * Convert from pjsip + */ + void fromPj(const pjsua_vid_preview_param &prm); + + /** + * Convert to pjsip + */ + pjsua_vid_preview_param toPj() const; +}; + +/** + * Video Preview + */ +class VideoPreview { +public: + /** + * Constructor + */ + VideoPreview(int dev_id); + + /** + * Determine if the specified video input device has built-in native + * preview capability. This is a convenience function that is equal to + * querying device's capability for PJMEDIA_VID_DEV_CAP_INPUT_PREVIEW + * capability. + * + * @return true if it has. + */ + bool hasNative(); + + /** + * Start video preview window for the specified capture device. + * + * @param p Video preview parameters. + */ + void start(const VideoPreviewOpParam ¶m) throw(Error); + + /** + * Stop video preview. + */ + void stop() throw(Error); + + /* + * Get the preview window handle associated with the capture device,if any. + */ + VideoWindow getVideoWindow(); + +private: + pjmedia_vid_dev_index devId; +}; + /************************************************************************* * Codec management */ diff --git a/pjsip/src/pjsua2/media.cpp b/pjsip/src/pjsua2/media.cpp index 18b75f13..12e67f52 100644 --- a/pjsip/src/pjsua2/media.cpp +++ b/pjsip/src/pjsua2/media.cpp @@ -1093,6 +1093,88 @@ void VideoWindow::setWindow(const VideoWindowHandle &win) throw(Error) PJ_UNUSED_ARG(win); #endif } +/////////////////////////////////////////////////////////////////////////////// + +VideoPreviewOpParam::VideoPreviewOpParam() +{ +#if PJSUA_HAS_VIDEO + pjsua_vid_preview_param vid_prev_param; + + pjsua_vid_preview_param_default(&vid_prev_param); + fromPj(vid_prev_param); +#endif +} + +void VideoPreviewOpParam::fromPj(const pjsua_vid_preview_param &prm) +{ +#if PJSUA_HAS_VIDEO + this->rendId = prm.rend_id; + this->show = PJ2BOOL(prm.show); + this->windowFlags = prm.wnd_flags; + this->format.id = prm.format.id; + this->format.type = prm.format.type; + this->window.type = prm.wnd.type; + this->window.handle.window = prm.wnd.info.window; +#else + PJ_UNUSED_ARG(prm); +#endif +} + +pjsua_vid_preview_param VideoPreviewOpParam::toPj() const +{ + pjsua_vid_preview_param param; +#if PJSUA_HAS_VIDEO + param.rend_id = this->rendId; + param.show = this->show; + param.wnd_flags = this->windowFlags; + param.format.id = this->format.id; + param.format.type = this->format.type; + param.wnd.type = this->window.type; + param.wnd.info.window = this->window.handle.window; +#endif + return param; +} + +VideoPreview::VideoPreview(int dev_id) +: devId(dev_id) +{ + +} + +bool VideoPreview::hasNative() +{ +#if PJSUA_HAS_VIDEO + return(PJ2BOOL(pjsua_vid_preview_has_native(devId))); +#else + return false; +#endif +} + +void VideoPreview::start(const VideoPreviewOpParam ¶m) throw(Error) +{ +#if PJSUA_HAS_VIDEO + pjsua_vid_preview_param prm = param.toPj(); + PJSUA2_CHECK_EXPR(pjsua_vid_preview_start(devId, &prm)); +#else + PJ_UNUSED_ARG(param); +#endif +} + +void VideoPreview::stop() throw(Error) +{ +#if PJSUA_HAS_VIDEO + pjsua_vid_preview_stop(devId); +#endif +} + +VideoWindow VideoPreview::getVideoWindow() +{ +#if PJSUA_HAS_VIDEO + return (VideoWindow(pjsua_vid_preview_get_win(devId))); +#else + return (VideoWindow(PJSUA_INVALID_ID)); +#endif +} /////////////////////////////////////////////////////////////////////////////// void CodecInfo::fromPj(const pjsua_codec_info &codec_info) |