diff --git a/res/menu/videocall_activity_menu.xml b/res/menu/videocall_activity_menu.xml new file mode 100644 index 000000000..eaae6cf7a --- /dev/null +++ b/res/menu/videocall_activity_menu.xml @@ -0,0 +1,11 @@ + + + + + + + + + + diff --git a/res/values/strings.xml b/res/values/strings.xml index 8f2d27a54..127e89c5c 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -1,5 +1,13 @@ + Display dialer + High resolution + Low resolution + Change resolution + Mute/Unmute camera + Disable camera + Enable camera + Terminate call Video settings Share my camera Initiate video calls diff --git a/src/org/linphone/BandwidthManager.java b/src/org/linphone/BandwidthManager.java new file mode 100644 index 000000000..cb1f4d101 --- /dev/null +++ b/src/org/linphone/BandwidthManager.java @@ -0,0 +1,81 @@ +/* +BandwithManager.java +Copyright (C) 2010 Belledonne Communications, Grenoble, France + +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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +package org.linphone; + +import org.linphone.core.LinphoneCall; +import org.linphone.core.LinphoneCallParams; +import org.linphone.core.LinphoneCore; +import org.linphone.core.VideoSize; + +public class BandwidthManager { + + public static final int HIGH_RESOLUTION = 0; + public static final int LOW_RESOLUTION = 1; + public static final int LOW_BANDWIDTH = 2; + + private static final int[][] bandwidthes = {{512,512}, {128,128}, {80,80}}; + private static BandwidthManager instance; + + private int currentProfile = LOW_RESOLUTION; // FIXME first profile never defined in C part + public int getCurrentProfile() {return currentProfile;} + + public static final synchronized BandwidthManager getInstance() { + if (instance == null) instance = new BandwidthManager(); + return instance; + } + + private BandwidthManager() {} + + public void changeTo(int profile) { + LinphoneCore lc = LinphoneService.instance().getLinphoneCore(); + LinphoneCall lCall = lc.getCurrentCall(); + LinphoneCallParams params = lCall.getCurrentParamsReadOnly().copy(); + + if (profile == LOW_BANDWIDTH) { // OR video disabled by settings? +// lc.enableVideo(false, false); + params.setVideoEnabled(false); + } else { + params.setVideoEnabled(true); + VideoSize targetVideoSize; + switch (profile) { + case LOW_RESOLUTION: + targetVideoSize = VideoSize.createStandard(VideoSize.HVGA); + break; + case HIGH_RESOLUTION: + targetVideoSize = VideoSize.createStandard(VideoSize.CIF); + break; + default: + throw new RuntimeException("profile not managed : " + profile); + } + + lc.setPreferredVideoSize(targetVideoSize); + VideoSize actualVideoSize = lc.getPreferredVideoSize(); + if (!targetVideoSize.equals(actualVideoSize)) { + lc.setPreferredVideoSize(VideoSize.createStandard(VideoSize.QCIF)); + } + } + + + lc.setUploadBandwidth(bandwidthes[profile][0]); + lc.setDownloadBandwidth(bandwidthes[profile][1]); + + lc.updateCall(lCall, params); + currentProfile = profile; + } +} diff --git a/src/org/linphone/DialerActivity.java b/src/org/linphone/DialerActivity.java index 321cef397..3f2aabadb 100644 --- a/src/org/linphone/DialerActivity.java +++ b/src/org/linphone/DialerActivity.java @@ -152,18 +152,22 @@ public class DialerActivity extends Activity implements LinphoneCoreListener { return true; } }); - + mAddVideo = (ImageButton) findViewById(R.id.AddVideo); mAddVideo.setOnClickListener(new OnClickListener() { public void onClick(View v) { LinphoneCore lLinphoneCore = LinphoneService.instance().getLinphoneCore(); - LinphoneCallParams params = lLinphoneCore.getCurrentCall().getCurrentParamsReadOnly(); + LinphoneCall lCall = lLinphoneCore.getCurrentCall(); + LinphoneCallParams params = lCall.getCurrentParamsReadOnly(); String msg; if (params.getVideoEnabled()) { msg = "In video call; going back to video call activity"; - startVideoView(); + startVideoView(VIDEO_VIEW_ACTIVITY); } else { msg = "Not in video call; should go try to reinvite with video"; + params.setVideoEnabled(true); + AndroidCameraRecord.setMuteCamera(false); + lLinphoneCore.updateCall(lCall, params); } Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT).show(); } @@ -228,7 +232,7 @@ public class DialerActivity extends Activity implements LinphoneCoreListener { } else { mCall.setEnabled(false); mHangup.setEnabled(!mCall.isEnabled()); - boolean prefVideoEnabled = getPref(getApplicationContext().getString(R.string.pref_video_enable_key)); + boolean prefVideoEnabled = mPref.getBoolean(getString(R.string.pref_video_enable_key), false); if (!prefVideoEnabled && !mCall.isEnabled()) { mAddVideo.setEnabled(true); } @@ -416,12 +420,10 @@ public class DialerActivity extends Activity implements LinphoneCoreListener { } } } - private void startVideoView() { - //start video view + private void startVideoView(int requestCode) { Intent lIntent = new Intent(); - lIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); lIntent.setClass(this, VideoCallActivity.class); - startActivityForResult(lIntent,VIDEO_VIEW_ACTIVITY); + startActivityForResult(lIntent,requestCode); } public void registrationState(final LinphoneCore lc, final LinphoneProxyConfig cfg,final LinphoneCore.RegistrationState state,final String smessage) {/*nop*/}; @@ -445,7 +447,12 @@ public class DialerActivity extends Activity implements LinphoneCoreListener { exitCallMode(); } else if (state == LinphoneCall.State.StreamsRunning) { if (LinphoneService.instance().getLinphoneCore().getCurrentCall().getCurrentParamsReadOnly().getVideoEnabled()) { - startVideoView(); + startVideoView(VIDEO_VIEW_ACTIVITY); + } + } else if (state == LinphoneCall.State.CallUpdated) { + if (LinphoneService.instance().getLinphoneCore().getCurrentCall().getCurrentParamsReadOnly().getVideoEnabled()) { + AndroidCameraRecord.invalidateParameters(); + finishActivity(VIDEO_VIEW_ACTIVITY); } } } @@ -520,17 +527,14 @@ public class DialerActivity extends Activity implements LinphoneCoreListener { routeAudioToSpeaker(); // Privacy setting to not share the user camera by default - boolean prefVideoEnable = getPref(getApplicationContext().getString(R.string.pref_video_enable_key)); - boolean prefAutomaticallyShareMyCamera = getPref(getApplicationContext().getString(R.string.pref_video_automatically_share_my_video_key)); + boolean prefVideoEnable = mPref.getBoolean(getString(R.string.pref_video_enable_key), false); + boolean prefAutomaticallyShareMyCamera = mPref.getBoolean(getString(R.string.pref_video_automatically_share_my_video_key), false); AndroidCameraRecord.setMuteCamera(!(prefVideoEnable && prefAutomaticallyShareMyCamera)); } public void newOutgoingCall(String aTo) { newOutgoingCall(aTo,null); } - private boolean getPref(String key) { - return PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).getBoolean(key, false); - } public synchronized void newOutgoingCall(String aTo, String displayName) { String lto = aTo; @@ -559,20 +563,21 @@ public class DialerActivity extends Activity implements LinphoneCoreListener { try { LinphoneCallParams lParams = lLinphoneCore.createDefaultCallParameters().copy(); - boolean prefVideoEnable = getPref(getApplicationContext().getString(R.string.pref_video_enable_key)); - boolean prefInitiateWithVideo = getPref(getApplicationContext().getString(R.string.pref_video_initiate_call_with_video_key)); + boolean prefVideoEnable = mPref.getBoolean(getString(R.string.pref_video_enable_key), false); + boolean prefInitiateWithVideo = mPref.getBoolean(getString(R.string.pref_video_initiate_call_with_video_key), false); if (prefVideoEnable && prefInitiateWithVideo && lParams.getVideoEnabled()) { - lParams.setVideoEnalbled(true); + AndroidCameraRecord.setMuteCamera(false); + lParams.setVideoEnabled(true); lLinphoneCore.inviteAddressWithParams(lAddress, lParams); } else { - lParams.setVideoEnalbled(false); + lParams.setVideoEnabled(false); lLinphoneCore.inviteAddressWithParams(lAddress, lParams); } } catch (LinphoneCoreException e) { Toast toast = Toast.makeText(DialerActivity.this ,String.format(getString(R.string.error_cannot_get_call_parameters),mAddress.getText().toString()) - , Toast.LENGTH_LONG); + ,Toast.LENGTH_LONG); toast.show(); return; } diff --git a/src/org/linphone/VideoCallActivity.java b/src/org/linphone/VideoCallActivity.java index 9c5466b17..6190366bc 100644 --- a/src/org/linphone/VideoCallActivity.java +++ b/src/org/linphone/VideoCallActivity.java @@ -21,18 +21,20 @@ package org.linphone; import org.linphone.core.AndroidCameraRecord; +import org.linphone.core.LinphoneCore; import android.app.Activity; +import android.content.Intent; import android.os.Bundle; import android.util.Log; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; import android.view.SurfaceView; -import android.view.View; -import android.widget.FrameLayout; public class VideoCallActivity extends Activity { SurfaceView mVideoView; SurfaceView mVideoCaptureView; - private static boolean firstLaunch = true; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -46,25 +48,97 @@ public class VideoCallActivity extends Activity { final int rotation = getWindowManager().getDefaultDisplay().getRotation(); AndroidCameraRecord.setOrientationCode(rotation); - if (!firstLaunch) workaroundCapturePreviewHiddenOnSubsequentRotations(); - AndroidCameraRecord.setSurfaceView(mVideoCaptureView); - firstLaunch = false; + mVideoCaptureView.setZOrderOnTop(true); } + private void rewriteToggleCameraItem(MenuItem item) { + if (AndroidCameraRecord.getCameraMuted()) { + item.setTitle(getString(R.string.menu_videocall_toggle_camera_enable)); + } else { + item.setTitle(getString(R.string.menu_videocall_toggle_camera_disable)); + } + } + + private void rewriteChangeResolutionItem(MenuItem item) { + switch (BandwidthManager.getInstance().getCurrentProfile()) { + case BandwidthManager.HIGH_RESOLUTION: + item.setTitle(getString(R.string.menu_videocall_change_resolution_when_high_resolution)); + break; + case BandwidthManager.LOW_RESOLUTION: + item.setTitle(getString(R.string.menu_videocall_change_resolution_when_low_resolution)); + break; + default: + throw new RuntimeException("Current profile is unknown " + BandwidthManager.getInstance().getCurrentProfile()); + } + } + - private void workaroundCapturePreviewHiddenOnSubsequentRotations() { - View view = findViewById(R.id.video_frame); - if (view == null) { - Log.e("Linphone", "Android BUG: video frame not found; mix with landscape???"); - return; + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the currently selected menu XML resource. + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.videocall_activity_menu, menu); + + rewriteToggleCameraItem(menu.findItem(R.id.videocall_menu_toggle_camera)); + rewriteChangeResolutionItem(menu.findItem(R.id.videocall_menu_change_resolution)); + + return true; + } + + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.videocall_menu_back_to_dialer: + finish(); + break; + case R.id.videocall_menu_change_resolution: + BandwidthManager manager = BandwidthManager.getInstance(); + switch (manager.getCurrentProfile()) { + case BandwidthManager.HIGH_RESOLUTION: + manager.changeTo(BandwidthManager.LOW_RESOLUTION); + break; + case BandwidthManager.LOW_RESOLUTION: + manager.changeTo(BandwidthManager.HIGH_RESOLUTION); + break; + default: + throw new RuntimeException("Current profile is unknown " + manager.getCurrentProfile()); + } + + rewriteChangeResolutionItem(item); + break; + case R.id.videocall_menu_terminate_call: + LinphoneCore lLinphoneCore = LinphoneService.instance().getLinphoneCore(); + if (lLinphoneCore.isIncall()) { + lLinphoneCore.terminateCall(lLinphoneCore.getCurrentCall()); + } + finish(); + break; + case R.id.videocall_menu_toggle_camera: + AndroidCameraRecord.toggleMute(); + rewriteToggleCameraItem(item); + break; + default: + Log.e(LinphoneService.TAG, "Unknown menu item ["+item+"]"); + break; } - FrameLayout frame = (FrameLayout) view; - frame.removeAllViews(); - frame.addView(mVideoCaptureView); - frame.addView(mVideoView); + return false; + } + protected void startprefActivity() { + Intent intent = new Intent(Intent.ACTION_MAIN); + intent.setClass(this, LinphonePreferencesActivity.class); + startActivity(intent); } + + @Override + protected void onDestroy() { + // TODO Auto-generated method stub + super.onDestroy(); + } + + } diff --git a/src/org/linphone/core/AndroidCameraRecord.java b/src/org/linphone/core/AndroidCameraRecord.java index c16ef065d..138912f57 100644 --- a/src/org/linphone/core/AndroidCameraRecord.java +++ b/src/org/linphone/core/AndroidCameraRecord.java @@ -18,10 +18,14 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package org.linphone.core; +import java.util.ArrayList; +import java.util.List; + import android.hardware.Camera; import android.hardware.Camera.ErrorCallback; import android.hardware.Camera.Parameters; import android.hardware.Camera.PreviewCallback; +import android.hardware.Camera.Size; import android.os.Build; import android.util.Log; import android.view.SurfaceHolder; @@ -44,8 +48,9 @@ public abstract class AndroidCameraRecord { private static boolean previewStarted; private static boolean parametersSet; protected static int orientationCode; - private static boolean mute; + private static boolean muted; private static final String tag="Linphone"; + private static List supportedVideoSizes; public AndroidCameraRecord() { // TODO check if another instance is loaded and kill it. @@ -66,7 +71,7 @@ public abstract class AndroidCameraRecord { * It will start automatically */ private void startPreview() { - if (mute) { + if (muted) { Log.d(tag, "Not starting preview as camera has been muted"); return; } @@ -100,6 +105,9 @@ public abstract class AndroidCameraRecord { Camera.Parameters parameters=camera.getParameters(); + if (supportedVideoSizes == null) { + supportedVideoSizes = camera.getParameters().getSupportedPreviewSizes(); + } parameters.setPreviewSize(width, height); parameters.setPreviewFrameRate(fps); @@ -174,6 +182,13 @@ public abstract class AndroidCameraRecord { } + private static void stopPreview() { + camera.setPreviewCallback(null); // TODO check if used whatever the SDK version + camera.stopPreview(); + camera.release(); + camera=null; + previewStarted = false; + } public static final void setSurfaceView(final SurfaceView sv) { SurfaceHolder holder = sv.getHolder(); @@ -187,11 +202,7 @@ public abstract class AndroidCameraRecord { Log.e(tag, "Video capture: illegal state: surface destroyed but camera is already null"); return; } - camera.setPreviewCallback(null); // TODO check if used whatever the SDK version - camera.stopPreview(); - camera.release(); - camera=null; - previewStarted = false; + stopPreview(); Log.w(tag, "Video capture Surface destroyed"); } @@ -235,19 +246,45 @@ public abstract class AndroidCameraRecord { } public static void setMuteCamera(boolean m) { - if (m == mute) return; + if (m == muted) return; - mute = m; - if (mute && previewStarted) { - camera.stopPreview(); + muted = m; + if (muted && previewStarted) { + stopPreview(); return; } - if (!mute) { + if (!muted) { instance.startPreview(); } } + + public static void toggleMute() { + setMuteCamera(!muted); + } + + public static List supportedVideoSizes() { + if (supportedVideoSizes != null) { + return new ArrayList(supportedVideoSizes); + } + + if (camera == null) { + camera = Camera.open(); + supportedVideoSizes = camera.getParameters().getSupportedPreviewSizes(); + camera.release(); + return supportedVideoSizes; + } + + throw new RuntimeException("Should not be there"); + } + + public static boolean getCameraMuted() { + return muted; + } + + public static void invalidateParameters() { + parametersSet = false; + stopPreview(); + } } - - diff --git a/src/org/linphone/core/LinphoneCallImpl.java b/src/org/linphone/core/LinphoneCallImpl.java index 4c9a770c9..b6194c26f 100644 --- a/src/org/linphone/core/LinphoneCallImpl.java +++ b/src/org/linphone/core/LinphoneCallImpl.java @@ -66,6 +66,4 @@ class LinphoneCallImpl implements LinphoneCall { public LinphoneCallParams getCurrentParamsReadWrite() { return getCurrentParamsReadOnly().copy(); } - - } diff --git a/src/org/linphone/core/LinphoneCallParamsImpl.java b/src/org/linphone/core/LinphoneCallParamsImpl.java index add724f21..1242ee996 100644 --- a/src/org/linphone/core/LinphoneCallParamsImpl.java +++ b/src/org/linphone/core/LinphoneCallParamsImpl.java @@ -34,7 +34,7 @@ public class LinphoneCallParamsImpl implements LinphoneCallParams { return getVideoEnabled(nativePtr); } - public void setVideoEnalbled(boolean b) { + public void setVideoEnabled(boolean b) { enableVideo(nativePtr, b); } diff --git a/src/org/linphone/core/LinphoneCoreImpl.java b/src/org/linphone/core/LinphoneCoreImpl.java index 91913e322..532a7ae2c 100644 --- a/src/org/linphone/core/LinphoneCoreImpl.java +++ b/src/org/linphone/core/LinphoneCoreImpl.java @@ -81,7 +81,10 @@ class LinphoneCoreImpl implements LinphoneCore { private native String getStunServer(long nativePtr); private native long createDefaultCallParams(long nativePtr); private native int updateCall(long ptrLc, long ptrCall, long ptrParams); - + private native void setUploadBandwidth(long nativePtr, int bw); + private native void setDownloadBandwidth(long nativePtr, int bw); + private native void setPreferredVideoSize(long nativePtr, int width, int heigth); + private native int[] getPreferredVideoSize(long nativePtr); private static String TAG = "LinphoneCore"; @@ -379,4 +382,27 @@ class LinphoneCoreImpl implements LinphoneCore { return updateCall(nativePtr, ptrCall, ptrParams); } + + + public void setUploadBandwidth(int bw) { + setUploadBandwidth(nativePtr, bw); + } + + public void setDownloadBandwidth(int bw) { + setDownloadBandwidth(nativePtr, bw); + } + + public void setPreferredVideoSize(VideoSize vSize) { + setPreferredVideoSize(nativePtr, vSize.getWidth(), vSize.getHeight()); + } + + public VideoSize getPreferredVideoSize() { + int[] nativeSize = getPreferredVideoSize(nativePtr); + + VideoSize vSize = new VideoSize(); + vSize.setWidth(nativeSize[0]); + vSize.setHeight(nativeSize[1]); + + return vSize; + } }