From 4fb072b0a1d9f64bf4236751d3ed1f877e243d7e Mon Sep 17 00:00:00 2001 From: Simon Morlat Date: Tue, 26 Oct 2010 11:13:29 +0200 Subject: [PATCH 01/38] adapt to api addons --- LinphoneCoreFactoryImpl.java | 12 ++++++++++++ LinphoneCoreImpl.java | 13 +++++++++++++ 2 files changed, 25 insertions(+) diff --git a/LinphoneCoreFactoryImpl.java b/LinphoneCoreFactoryImpl.java index 712e27b63..e86f0b100 100644 --- a/LinphoneCoreFactoryImpl.java +++ b/LinphoneCoreFactoryImpl.java @@ -68,4 +68,16 @@ public class LinphoneCoreFactoryImpl extends LinphoneCoreFactory { //not implemented on Android } + + @Override + LinphoneFriend createLinphoneFriend(String friendUri) { + // TODO Auto-generated method stub + return null; + } + + @Override + LinphoneFriend createLinphoneFriend() { + // TODO Auto-generated method stub + return null; + } } diff --git a/LinphoneCoreImpl.java b/LinphoneCoreImpl.java index e042b3951..223a52fdc 100644 --- a/LinphoneCoreImpl.java +++ b/LinphoneCoreImpl.java @@ -270,5 +270,18 @@ class LinphoneCoreImpl implements LinphoneCore { public void stopDtmf() { stopDtmf(nativePtr); } + public void addFriend(LinphoneFriend lf) throws LinphoneCoreException { + // TODO Auto-generated method stub + + } + public LinphoneChatRoom createChatRoom(String to) { + // TODO Auto-generated method stub + return null; + } + public void setPresenceInfo(int minuteAway, String alternativeContact, + OnlineStatus status) { + // TODO Auto-generated method stub + + } } From ff57fa0a2891ce3cc9de773a8c922e7ffa7547c3 Mon Sep 17 00:00:00 2001 From: Simon Morlat Date: Mon, 1 Nov 2010 11:33:21 +0100 Subject: [PATCH 02/38] video in progress --- AndroidVideoWindowImpl.java | 75 +++++++++++++++++++++++++++++++++++++ LinphoneCoreImpl.java | 32 ++++++++++++++++ 2 files changed, 107 insertions(+) create mode 100644 AndroidVideoWindowImpl.java diff --git a/AndroidVideoWindowImpl.java b/AndroidVideoWindowImpl.java new file mode 100644 index 000000000..f1927e184 --- /dev/null +++ b/AndroidVideoWindowImpl.java @@ -0,0 +1,75 @@ +package org.linphone.core; + +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Bitmap.Config; +import android.view.Surface; +import android.view.SurfaceHolder; +import android.view.SurfaceView; +import android.view.Surface.OutOfResourcesException; +import android.view.SurfaceHolder.Callback; + +public class AndroidVideoWindowImpl implements VideoWindow { + private Bitmap mBitmap; + private SurfaceView mView; + private Surface mSurface; + private VideoWindowListener mListener; + public static interface VideoWindowListener{ + void onSurfaceReady(AndroidVideoWindowImpl vw); + void onSurfaceDestroyed(AndroidVideoWindowImpl vw); + }; + public AndroidVideoWindowImpl(SurfaceView view){ + mView=view; + mBitmap=null; + mSurface=null; + mListener=null; + view.getHolder().addCallback(new Callback(){ + public void surfaceChanged(SurfaceHolder holder, int format, + int width, int height) { + synchronized(AndroidVideoWindowImpl.this){ + mBitmap=Bitmap.createBitmap(width,height,Config.RGB_565); + if (mListener!=null) mListener.onSurfaceReady(AndroidVideoWindowImpl.this); + mSurface=holder.getSurface(); + } + } + + public void surfaceCreated(SurfaceHolder holder) { + } + + public void surfaceDestroyed(SurfaceHolder holder) { + synchronized(AndroidVideoWindowImpl.this){ + mBitmap=null; + if (mListener!=null) + mListener.onSurfaceDestroyed(AndroidVideoWindowImpl.this); + mSurface=null; + } + } + }); + } + public void setListener(VideoWindowListener l){ + mListener=l; + } + public Surface getSurface(){ + return mView.getHolder().getSurface(); + } + public Bitmap getBitmap(){ + return mBitmap; + } + //Called by the mediastreamer2 android display filter + public synchronized void update(){ + if (mSurface!=null){ + try { + Canvas canvas=mSurface.lockCanvas(null); + canvas.drawBitmap(mBitmap, 0, 0, null); + mSurface.unlockCanvasAndPost(canvas); + + } catch (IllegalArgumentException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (OutOfResourcesException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + } +} diff --git a/LinphoneCoreImpl.java b/LinphoneCoreImpl.java index 223a52fdc..aad67304a 100644 --- a/LinphoneCoreImpl.java +++ b/LinphoneCoreImpl.java @@ -63,6 +63,10 @@ class LinphoneCoreImpl implements LinphoneCore { private native long getCurrentCall(long nativePtr) ; private native void playDtmf(long nativePtr,char dtmf,int duration); private native void stopDtmf(long nativePtr); + private native void setVideoWindowId(long nativePtr, Object wid); + private native void setPreviewWindowId(long nativePtr, Object wid); + private AndroidVideoWindowImpl mVideoWindow; + private AndroidVideoWindowImpl mPreviewWindow; LinphoneCoreImpl(LinphoneCoreListener listener, File userConfig,File factoryConfig,Object userdata) throws IOException { mListener=listener; @@ -283,5 +287,33 @@ class LinphoneCoreImpl implements LinphoneCore { // TODO Auto-generated method stub } + public void setPreviewWindow(VideoWindow w) { + if (mPreviewWindow!=null) + mPreviewWindow.setListener(null); + mPreviewWindow=(AndroidVideoWindowImpl)w; + mPreviewWindow.setListener(new AndroidVideoWindowImpl.VideoWindowListener(){ + public void onSurfaceDestroyed(AndroidVideoWindowImpl vw) { + setPreviewWindowId(nativePtr,null); + } + + public void onSurfaceReady(AndroidVideoWindowImpl vw) { + setPreviewWindowId(nativePtr,vw); + } + }); + } + public void setVideoWindow(VideoWindow w) { + if (mVideoWindow!=null) + mVideoWindow.setListener(null); + mVideoWindow=(AndroidVideoWindowImpl)w; + mVideoWindow.setListener(new AndroidVideoWindowImpl.VideoWindowListener(){ + public void onSurfaceDestroyed(AndroidVideoWindowImpl vw) { + setVideoWindowId(nativePtr,null); + } + + public void onSurfaceReady(AndroidVideoWindowImpl vw) { + setVideoWindowId(nativePtr,vw); + } + }); + } } From c909af1791c901c7ea656273b978f26c9a40f061 Mon Sep 17 00:00:00 2001 From: Jehan Monnier Date: Fri, 12 Nov 2010 09:01:59 +0100 Subject: [PATCH 03/38] add video call activity still to be debugged --- AndroidVideoWindowImpl.java | 2 +- LinphoneCoreImpl.java | 17 ++++++++++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/AndroidVideoWindowImpl.java b/AndroidVideoWindowImpl.java index f1927e184..2bba92d72 100644 --- a/AndroidVideoWindowImpl.java +++ b/AndroidVideoWindowImpl.java @@ -9,7 +9,7 @@ import android.view.SurfaceView; import android.view.Surface.OutOfResourcesException; import android.view.SurfaceHolder.Callback; -public class AndroidVideoWindowImpl implements VideoWindow { +public class AndroidVideoWindowImpl implements Object { private Bitmap mBitmap; private SurfaceView mView; private Surface mSurface; diff --git a/LinphoneCoreImpl.java b/LinphoneCoreImpl.java index 657cdc407..e8d13a4cd 100644 --- a/LinphoneCoreImpl.java +++ b/LinphoneCoreImpl.java @@ -22,6 +22,8 @@ import java.io.File; import java.io.IOException; import java.util.Vector; +import android.view.SurfaceView; + class LinphoneCoreImpl implements LinphoneCore { @@ -71,6 +73,9 @@ class LinphoneCoreImpl implements LinphoneCore { private native void addFriend(long nativePtr,long friend); private native void setPresenceInfo(long nativePtr,int minute_away, String alternative_contact,int status); private native long createChatRoom(long nativePtr,String to); + private native void enableVideo(long nativePtr,boolean vcap_enabled,boolean display_enabled); + private native boolean isVideoEnabled(long nativePtr); + LinphoneCoreImpl(LinphoneCoreListener listener, File userConfig,File factoryConfig,Object userdata) throws IOException { mListener=listener; @@ -296,10 +301,10 @@ class LinphoneCoreImpl implements LinphoneCore { public LinphoneChatRoom createChatRoom(String to) { return new LinphoneChatRoomImpl(createChatRoom(nativePtr,to)); } - public void setPreviewWindow(VideoWindow w) { + public void setPreviewWindow(Object w) { if (mPreviewWindow!=null) mPreviewWindow.setListener(null); - mPreviewWindow=(AndroidVideoWindowImpl)w; + mPreviewWindow=new AndroidVideoWindowImpl((SurfaceView)w); mPreviewWindow.setListener(new AndroidVideoWindowImpl.VideoWindowListener(){ public void onSurfaceDestroyed(AndroidVideoWindowImpl vw) { setPreviewWindowId(nativePtr,null); @@ -310,7 +315,7 @@ class LinphoneCoreImpl implements LinphoneCore { } }); } - public void setVideoWindow(VideoWindow w) { + public void setVideoWindow(Object w) { if (mVideoWindow!=null) mVideoWindow.setListener(null); mVideoWindow=(AndroidVideoWindowImpl)w; @@ -324,5 +329,11 @@ class LinphoneCoreImpl implements LinphoneCore { } }); } + public void enableVideo(boolean vcap_enabled, boolean display_enabled) { + enableVideo(nativePtr,vcap_enabled, display_enabled); + } + public boolean isVideoEnabled() { + return isVideoEnabled(nativePtr); + } } From aa8101f5bfc895e959857cac952c51f394639a9f Mon Sep 17 00:00:00 2001 From: Jehan Monnier Date: Fri, 12 Nov 2010 09:52:56 +0100 Subject: [PATCH 04/38] fix video view --- AndroidVideoWindowImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AndroidVideoWindowImpl.java b/AndroidVideoWindowImpl.java index 2bba92d72..2bb4314f5 100644 --- a/AndroidVideoWindowImpl.java +++ b/AndroidVideoWindowImpl.java @@ -9,7 +9,7 @@ import android.view.SurfaceView; import android.view.Surface.OutOfResourcesException; import android.view.SurfaceHolder.Callback; -public class AndroidVideoWindowImpl implements Object { +public class AndroidVideoWindowImpl { private Bitmap mBitmap; private SurfaceView mView; private Surface mSurface; From ec2fd191a8bdc04572f5b417a2d64d456c902054 Mon Sep 17 00:00:00 2001 From: Guillaume Beraudo Date: Fri, 12 Nov 2010 14:11:50 +0100 Subject: [PATCH 05/38] Fixed wrong cast --- LinphoneCoreImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LinphoneCoreImpl.java b/LinphoneCoreImpl.java index e8d13a4cd..da53636bc 100644 --- a/LinphoneCoreImpl.java +++ b/LinphoneCoreImpl.java @@ -318,7 +318,7 @@ class LinphoneCoreImpl implements LinphoneCore { public void setVideoWindow(Object w) { if (mVideoWindow!=null) mVideoWindow.setListener(null); - mVideoWindow=(AndroidVideoWindowImpl)w; + mVideoWindow=new AndroidVideoWindowImpl((SurfaceView) w); mVideoWindow.setListener(new AndroidVideoWindowImpl.VideoWindowListener(){ public void onSurfaceDestroyed(AndroidVideoWindowImpl vw) { setVideoWindowId(nativePtr,null); From e9b5957dc3c0660059dc6e2e326fa0e9ccb5475d Mon Sep 17 00:00:00 2001 From: Guillaume Beraudo Date: Fri, 12 Nov 2010 14:16:01 +0100 Subject: [PATCH 06/38] Draft for Android video capture. --- AndroidCameraRecord.java | 127 ++++++++++++++++++++++++++++ AndroidCameraRecordImpl.java | 49 +++++++++++ tutorials/JavaCameraRecordImpl.java | 69 +++++++++++++++ tutorials/TestVideoActivity.java | 70 +++++++++++++++ 4 files changed, 315 insertions(+) create mode 100644 AndroidCameraRecord.java create mode 100644 AndroidCameraRecordImpl.java create mode 100644 tutorials/JavaCameraRecordImpl.java create mode 100644 tutorials/TestVideoActivity.java diff --git a/AndroidCameraRecord.java b/AndroidCameraRecord.java new file mode 100644 index 000000000..ae07c6d56 --- /dev/null +++ b/AndroidCameraRecord.java @@ -0,0 +1,127 @@ +/* +AndroidCameraRecordImpl.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.core; + +import android.hardware.Camera; +import android.hardware.Camera.PreviewCallback; +import android.util.Log; +import android.view.SurfaceHolder; +import android.view.SurfaceView; + + +public abstract class AndroidCameraRecord implements SurfaceHolder.Callback { + + protected Camera camera; + private static SurfaceView surfaceView; // should be initialized first... + protected int rate; + private int visibility = SurfaceView.GONE; // Automatically hidden + private boolean visibilityChangeable = false; + + protected final SurfaceView getSurfaceView() { + return surfaceView; + } + + /** + * AndroidCameraRecord.setSurfaceView() should be called first. + * @param rate + */ + public AndroidCameraRecord(int rate) { + camera=Camera.open(); + SurfaceHolder holder = surfaceView.getHolder(); + holder.addCallback(this); + + this.rate = rate; + } + + + /** + * AndroidCameraRecord.setSurfaceView() should be called first. + * @param rate + * @param visilibity + */ + public AndroidCameraRecord(int rate, int visilibity) { + this(rate); + this.visibility = visilibity; + } + + + public void surfaceCreated(SurfaceHolder holder) { + try { + camera.setPreviewDisplay(holder); + } + catch (Throwable t) { + Log.e("PictureDemo-surfaceCallback", "Exception in setPreviewDisplay()", t); + } + + + } + + public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { + Camera.Parameters parameters=camera.getParameters(); + + parameters.setPreviewSize(width, height); + parameters.setPreviewFrameRate(rate); + camera.setParameters(parameters); + + camera.startPreview(); + + visibilityChangeable = true; + if (surfaceView.getVisibility() != visibility) { + updateVisibility(); + } + + } + + public void surfaceDestroyed(SurfaceHolder holder) { + camera.stopPreview(); + camera.release(); + camera=null; + } + + public void setPreviewCallBack(PreviewCallback cb) { + camera.setPreviewCallback(cb); + } + + private void updateVisibility() { + if (!visibilityChangeable) { + throw new IllegalStateException("Visilibity not changeable now"); + } + + surfaceView.setVisibility(visibility); + } + + public void setVisibility(int visibility) { + if (visibility == this.visibility) return; + + this.visibility = visibility; + updateVisibility(); + } + + public static final void setSurfaceView(SurfaceView sv) { + AndroidCameraRecord.surfaceView = sv; + } + + + /** + * Hook to add back a buffer for reuse in capture. + * Override in a version supporting addPreviewCallBackWithBuffer() + * @param buffer buffer to reuse + */ + public void addBackCaptureBuffer(byte[] buffer) {} +} diff --git a/AndroidCameraRecordImpl.java b/AndroidCameraRecordImpl.java new file mode 100644 index 000000000..c9173d1da --- /dev/null +++ b/AndroidCameraRecordImpl.java @@ -0,0 +1,49 @@ +/* +AndroidCameraRecordImpl.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.core; + +import android.hardware.Camera; +import android.hardware.Camera.PreviewCallback; +import android.util.Log; + +/** + * Record from Android camera. + * + * @author Guillaume Beraudo + * + */ +public class AndroidCameraRecordImpl extends AndroidCameraRecord implements PreviewCallback { + + private long filterCtxPtr; + + public AndroidCameraRecordImpl(long filterCtxPtr, int rate) { + super(rate); + this.filterCtxPtr = filterCtxPtr; + setPreviewCallBack(this); + } + + + private native void putImage(long filterCtxPtr, byte[] buffer); + + public void onPreviewFrame(byte[] data, Camera camera) { + Log.d("onPreviewFrame: ", Integer.toString(data.length)); + putImage(filterCtxPtr, data); + } + +} diff --git a/tutorials/JavaCameraRecordImpl.java b/tutorials/JavaCameraRecordImpl.java new file mode 100644 index 000000000..45499db78 --- /dev/null +++ b/tutorials/JavaCameraRecordImpl.java @@ -0,0 +1,69 @@ +/* +JavaCameraRecordImpl.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.core.tutorials; + +import org.linphone.core.AndroidCameraRecord; + +import android.hardware.Camera; +import android.hardware.Camera.PreviewCallback; +import android.util.Log; +import android.widget.TextView; + +public class JavaCameraRecordImpl extends AndroidCameraRecord implements PreviewCallback { + + private TextView debug; + private long count = 0; + private float averageCalledRate; + private int averageWindowSize = 2 * rate; + + private long startTime; + private long endTime; + + + public JavaCameraRecordImpl(int rate) { + super(rate); + setPreviewCallBack(this); + } + + public JavaCameraRecordImpl(int rate, int visilibity) { + super(rate, visilibity); + setPreviewCallBack(this); + } + + public void setDebug(TextView debug) { + this.debug = debug; + } + + public void onPreviewFrame(byte[] data, Camera camera) { + + if ((count % averageWindowSize) == 0) { + endTime = System.currentTimeMillis(); + averageCalledRate = (100000 * averageWindowSize) / (endTime - startTime); + averageCalledRate /= 100f; + startTime = endTime; + } + + count++; + + String msg = "Frame " + count + ": " + data.length + "bytes (avg="+averageCalledRate+"frames/s)"; + if (debug != null) debug.setText(msg); + Log.d("onPreviewFrame:", msg); + } + +} diff --git a/tutorials/TestVideoActivity.java b/tutorials/TestVideoActivity.java new file mode 100644 index 000000000..6526e1cce --- /dev/null +++ b/tutorials/TestVideoActivity.java @@ -0,0 +1,70 @@ +/* +TutorialHelloWorldActivity.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.core.tutorials; + +import org.linphone.R; +import org.linphone.core.AndroidCameraRecord; + +import android.app.Activity; +import android.os.Bundle; +import android.view.SurfaceHolder; +import android.view.SurfaceView; +import android.widget.TextView; + +/** + * Activity for displaying and starting the HelloWorld example on Android phone. + * + * @author Guillaume Beraudo + * + */ +public class TestVideoActivity extends Activity { + + private SurfaceView surfaceView; + private static final int rate = 15; + + + + + + + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.videotest); + + surfaceView=(SurfaceView)findViewById(R.id.videotest_surfaceView); + + SurfaceHolder holder=surfaceView.getHolder(); + holder.setFixedSize(320, 240); + holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); + + AndroidCameraRecord.setSurfaceView(surfaceView); + + JavaCameraRecordImpl manager = new JavaCameraRecordImpl(rate, SurfaceView.VISIBLE); + manager.setDebug((TextView) findViewById(R.id.videotest_debug)); + + } + + + + + + +} From d047b7aabd677fd48963f4166388abb86d15678e Mon Sep 17 00:00:00 2001 From: Simon Morlat Date: Sun, 14 Nov 2010 19:15:27 +0100 Subject: [PATCH 07/38] bugfixes and makefile improvements --- AndroidVideoWindowImpl.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/AndroidVideoWindowImpl.java b/AndroidVideoWindowImpl.java index 2bb4314f5..87e1dc525 100644 --- a/AndroidVideoWindowImpl.java +++ b/AndroidVideoWindowImpl.java @@ -28,8 +28,9 @@ public class AndroidVideoWindowImpl { int width, int height) { synchronized(AndroidVideoWindowImpl.this){ mBitmap=Bitmap.createBitmap(width,height,Config.RGB_565); - if (mListener!=null) mListener.onSurfaceReady(AndroidVideoWindowImpl.this); mSurface=holder.getSurface(); + if (mListener!=null) mListener.onSurfaceReady(AndroidVideoWindowImpl.this); + } } @@ -38,10 +39,10 @@ public class AndroidVideoWindowImpl { public void surfaceDestroyed(SurfaceHolder holder) { synchronized(AndroidVideoWindowImpl.this){ - mBitmap=null; if (mListener!=null) mListener.onSurfaceDestroyed(AndroidVideoWindowImpl.this); mSurface=null; + mBitmap=null; } } }); From 97144546e2c1f0263576ae8e792a3f68a8b1bfec Mon Sep 17 00:00:00 2001 From: Simon Morlat Date: Mon, 15 Nov 2010 19:19:32 +0100 Subject: [PATCH 08/38] fix video deadlock and double instanciation of VideoActivity --- AndroidVideoWindowImpl.java | 11 +++++++---- LinphoneCoreImpl.java | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/AndroidVideoWindowImpl.java b/AndroidVideoWindowImpl.java index 87e1dc525..efac2d826 100644 --- a/AndroidVideoWindowImpl.java +++ b/AndroidVideoWindowImpl.java @@ -3,6 +3,7 @@ package org.linphone.core; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Bitmap.Config; +import android.util.Log; import android.view.Surface; import android.view.SurfaceHolder; import android.view.SurfaceView; @@ -14,6 +15,7 @@ public class AndroidVideoWindowImpl { private SurfaceView mView; private Surface mSurface; private VideoWindowListener mListener; + static private String TAG = "Linphone"; public static interface VideoWindowListener{ void onSurfaceReady(AndroidVideoWindowImpl vw); void onSurfaceDestroyed(AndroidVideoWindowImpl vw); @@ -26,12 +28,12 @@ public class AndroidVideoWindowImpl { view.getHolder().addCallback(new Callback(){ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { + Log.i(TAG,"Surface is being changed."); synchronized(AndroidVideoWindowImpl.this){ mBitmap=Bitmap.createBitmap(width,height,Config.RGB_565); mSurface=holder.getSurface(); - if (mListener!=null) mListener.onSurfaceReady(AndroidVideoWindowImpl.this); - } + if (mListener!=null) mListener.onSurfaceReady(AndroidVideoWindowImpl.this); } public void surfaceCreated(SurfaceHolder holder) { @@ -39,11 +41,11 @@ public class AndroidVideoWindowImpl { public void surfaceDestroyed(SurfaceHolder holder) { synchronized(AndroidVideoWindowImpl.this){ - if (mListener!=null) - mListener.onSurfaceDestroyed(AndroidVideoWindowImpl.this); mSurface=null; mBitmap=null; } + if (mListener!=null) + mListener.onSurfaceDestroyed(AndroidVideoWindowImpl.this); } }); } @@ -74,3 +76,4 @@ public class AndroidVideoWindowImpl { } } } + diff --git a/LinphoneCoreImpl.java b/LinphoneCoreImpl.java index da53636bc..c96373cb8 100644 --- a/LinphoneCoreImpl.java +++ b/LinphoneCoreImpl.java @@ -75,7 +75,7 @@ class LinphoneCoreImpl implements LinphoneCore { private native long createChatRoom(long nativePtr,String to); private native void enableVideo(long nativePtr,boolean vcap_enabled,boolean display_enabled); private native boolean isVideoEnabled(long nativePtr); - + private static String TAG = "LinphoneCore"; LinphoneCoreImpl(LinphoneCoreListener listener, File userConfig,File factoryConfig,Object userdata) throws IOException { mListener=listener; From 37842c4b7cb6158a464eaf03542b22276a4e1f19 Mon Sep 17 00:00:00 2001 From: Guillaume Beraudo Date: Wed, 17 Nov 2010 15:52:42 +0100 Subject: [PATCH 09/38] First working Android video version. --- AndroidCameraRecord.java | 230 ++++++++++++++++++++------- AndroidCameraRecordBufferedImpl.java | 69 ++++++++ AndroidCameraRecordImpl.java | 55 ++++++- tutorials/JavaCameraRecordImpl.java | 15 +- tutorials/TestVideoActivity.java | 19 +-- 5 files changed, 302 insertions(+), 86 deletions(-) create mode 100644 AndroidCameraRecordBufferedImpl.java diff --git a/AndroidCameraRecord.java b/AndroidCameraRecord.java index ae07c6d56..6ebce9856 100644 --- a/AndroidCameraRecord.java +++ b/AndroidCameraRecord.java @@ -19,102 +19,176 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. package org.linphone.core; import android.hardware.Camera; +import android.hardware.Camera.ErrorCallback; import android.hardware.Camera.PreviewCallback; +import android.os.Build; +import android.os.Handler; import android.util.Log; import android.view.SurfaceHolder; import android.view.SurfaceView; +import android.view.SurfaceHolder.Callback; -public abstract class AndroidCameraRecord implements SurfaceHolder.Callback { +public abstract class AndroidCameraRecord { + + public static final int ANDROID_VERSION = Integer.parseInt(Build.VERSION.SDK); + protected static Camera camera; + private static SurfaceView surfaceView; + + protected int fps; + protected int height; + protected int width; + private int longTermVisibility; - protected Camera camera; - private static SurfaceView surfaceView; // should be initialized first... - protected int rate; - private int visibility = SurfaceView.GONE; // Automatically hidden private boolean visibilityChangeable = false; + private PreviewCallback storedPreviewCallback; - protected final SurfaceView getSurfaceView() { - return surfaceView; + private static AndroidCameraRecord instance; + private static Handler handler; + private static boolean previewStarted; + + public AndroidCameraRecord() { + // TODO check if another instance is loaded and kill it. + instance = this; } - /** - * AndroidCameraRecord.setSurfaceView() should be called first. - * @param rate + public void setParameters(int height, int width, float fps, boolean hide) { + this.fps = Math.round(fps); + this.height = height; + this.width = width; + this.longTermVisibility = hide ? SurfaceView.GONE : SurfaceView.VISIBLE; + + if (surfaceView != null) { + Log.d("Linphone", "Surfaceview defined and ready; starting video capture"); + instance.startPreview(); + } else { + Log.w("Linphone", "Surfaceview not defined; postponning video capture"); + } + } + + /* + * AndroidCameraRecord.setSurfaceView() should be called first, from the Activity code. + * It will start automatically */ - public AndroidCameraRecord(int rate) { + private void startPreview() { + assert surfaceView != null; + + if (previewStarted) { + Log.w("Linphone", "Already started"); + return; + } + + if (surfaceView.getVisibility() != SurfaceView.VISIBLE) { + // Illegal state + Log.e("Linphone", "Illegal state: video capture surface view is not visible"); + return; + } + + camera=Camera.open(); + camera.setErrorCallback(new ErrorCallback() { + public void onError(int error, Camera camera) { + Log.e("Linphone", "Camera error : " + error); + } + }); + + + Camera.Parameters parameters=camera.getParameters(); + + parameters.setPreviewSize(width, height); + parameters.setPreviewFrameRate(fps); + camera.setParameters(parameters); + + SurfaceHolder holder = surfaceView.getHolder(); - holder.addCallback(this); - - this.rate = rate; - } - - - /** - * AndroidCameraRecord.setSurfaceView() should be called first. - * @param rate - * @param visilibity - */ - public AndroidCameraRecord(int rate, int visilibity) { - this(rate); - this.visibility = visilibity; - } - - - public void surfaceCreated(SurfaceHolder holder) { try { camera.setPreviewDisplay(holder); } catch (Throwable t) { - Log.e("PictureDemo-surfaceCallback", "Exception in setPreviewDisplay()", t); + Log.e("Linphone", "Exception in Video capture setPreviewDisplay()", t); } - } + try { + camera.startPreview(); + previewStarted = true; + } catch (Throwable e) { + Log.e("Linphone", "Can't start camera preview"); + } - public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { - Camera.Parameters parameters=camera.getParameters(); + previewStarted = true; - parameters.setPreviewSize(width, height); - parameters.setPreviewFrameRate(rate); - camera.setParameters(parameters); - - camera.startPreview(); + + + // Register callback to get capture buffer + if (storedPreviewCallback != null) { + reallySetPreviewCallback(camera, storedPreviewCallback); + } + visibilityChangeable = true; - if (surfaceView.getVisibility() != visibility) { + if (surfaceView.getVisibility() != longTermVisibility) { updateVisibility(); } - + + onCameraStarted(camera); } + - public void surfaceDestroyed(SurfaceHolder holder) { - camera.stopPreview(); - camera.release(); - camera=null; - } - public void setPreviewCallBack(PreviewCallback cb) { - camera.setPreviewCallback(cb); - } - private void updateVisibility() { - if (!visibilityChangeable) { - throw new IllegalStateException("Visilibity not changeable now"); + /** + * Hook. + * @param camera + */ + public void onCameraStarted(Camera camera) {} + + public void setOrStorePreviewCallBack(PreviewCallback cb) { + if (camera == null) { + Log.w("Linphone", "Capture camera not ready, storing callback"); + this.storedPreviewCallback = cb; + return; } - - surfaceView.setVisibility(visibility); + + reallySetPreviewCallback(camera, cb); } - public void setVisibility(int visibility) { - if (visibility == this.visibility) return; - this.visibility = visibility; - updateVisibility(); - } + + public static final void setSurfaceView(final SurfaceView sv, Handler mHandler) { + AndroidCameraRecord.handler = mHandler; + SurfaceHolder holder = sv.getHolder(); + holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); - public static final void setSurfaceView(SurfaceView sv) { - AndroidCameraRecord.surfaceView = sv; + holder.addCallback(new Callback() { + public void surfaceDestroyed(SurfaceHolder holder) { + AndroidCameraRecord.surfaceView = null; + + if (camera == null) { + Log.e("AndroidCameraRecord.surfaceDestroyed", "illegal state"); + return; + } + camera.setPreviewCallback(null); // TODO check if used whatever the SDK version + camera.stopPreview(); + camera.release(); + camera=null; + previewStarted = false; + Log.w("Linphone", "The video capture Surface view has been destroyed"); + } + + public void surfaceCreated(SurfaceHolder holder) { + AndroidCameraRecord.surfaceView = sv; + + if (instance != null) { + instance.startPreview(); + } + } + + public void surfaceChanged(SurfaceHolder holder, int format, int width, + int height) { + // Do nothing + } + }); } @@ -124,4 +198,40 @@ public abstract class AndroidCameraRecord implements SurfaceHolder.Callback { * @param buffer buffer to reuse */ public void addBackCaptureBuffer(byte[] buffer) {} + + + + private void updateVisibility() { + if (!visibilityChangeable) { + throw new IllegalStateException("Visilibity not changeable now"); + } + + handler.post(new Runnable() { + public void run() { + Log.d("Linphone", "Changing video capture surface view visibility :" + longTermVisibility); + surfaceView.setVisibility(longTermVisibility); + } + }); + } + + public void setVisibility(int visibility) { + if (visibility == this.longTermVisibility) return; + + this.longTermVisibility = visibility; + updateVisibility(); + } + + + public void stopCaptureCallback() { + if (camera != null) { + reallySetPreviewCallback(camera, null); + } + } + + protected void reallySetPreviewCallback(Camera camera, PreviewCallback cb) { + camera.setPreviewCallback(cb); + } + } + + diff --git a/AndroidCameraRecordBufferedImpl.java b/AndroidCameraRecordBufferedImpl.java new file mode 100644 index 000000000..c65f479ad --- /dev/null +++ b/AndroidCameraRecordBufferedImpl.java @@ -0,0 +1,69 @@ +/* +AndroidCameraRecord8Impl.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.core; + +import android.hardware.Camera; +import android.hardware.Camera.PreviewCallback; +import android.hardware.Camera.Size; +import android.util.Log; + +/** + * + * Android >= 8 (2.2) version. + * @author Guillaume Beraudo + * + */ +public class AndroidCameraRecordBufferedImpl extends AndroidCameraRecordImpl { + + public AndroidCameraRecordBufferedImpl(long filterCtxPtr) { + super(filterCtxPtr); + } + + @Override + protected void reallySetPreviewCallback(Camera camera, PreviewCallback cb) { + Log.d("Linphone", "Setting optimized callback with buffer (Android >= 8). Remember to manage the pool of buffers!!!"); + camera.setPreviewCallbackWithBuffer(cb); + } + + @Override + public void addBackCaptureBuffer(byte[] buffer) { + camera.addCallbackBuffer(buffer); + } + + @Override + public void onCameraStarted(Camera camera) { + super.onCameraStarted(camera); + + Size s = camera.getParameters().getPreviewSize(); + int wishedBufferSize = s.height * s.width * 3 / 2; + + camera.addCallbackBuffer(new byte[wishedBufferSize]); + camera.addCallbackBuffer(new byte[wishedBufferSize]); +/* + for (int i=1; i < 30; i++) { + camera.addCallbackBuffer(new byte[wishedBufferSize]); + }*/ + } + + @Override + protected void badBufferLengthReceived(byte[] buffer, int expectedBufferLength) { + super.badBufferLengthReceived(buffer, expectedBufferLength); + addBackCaptureBuffer(buffer); + } +} diff --git a/AndroidCameraRecordImpl.java b/AndroidCameraRecordImpl.java index c9173d1da..aab29a183 100644 --- a/AndroidCameraRecordImpl.java +++ b/AndroidCameraRecordImpl.java @@ -20,6 +20,7 @@ package org.linphone.core; import android.hardware.Camera; import android.hardware.Camera.PreviewCallback; +import android.hardware.Camera.Size; import android.util.Log; /** @@ -31,19 +32,63 @@ import android.util.Log; public class AndroidCameraRecordImpl extends AndroidCameraRecord implements PreviewCallback { private long filterCtxPtr; + private double timeElapsedBetweenFrames = 0; + private long lastFrameTime = 0; - public AndroidCameraRecordImpl(long filterCtxPtr, int rate) { - super(rate); - this.filterCtxPtr = filterCtxPtr; - setPreviewCallBack(this); + public AndroidCameraRecordImpl(long filterCtxPtr) { + super(); + + try { + this.filterCtxPtr = filterCtxPtr; + setOrStorePreviewCallBack(this); + } catch (Throwable e) { + Log.e("Linphone", "Error"); + } + } private native void putImage(long filterCtxPtr, byte[] buffer); + public void onPreviewFrame(byte[] data, Camera camera) { - Log.d("onPreviewFrame: ", Integer.toString(data.length)); + if (data == null) { + Log.e("Linphone", "onPreviewFrame Called with null buffer"); + return; + } + + Size s = camera.getParameters().getPreviewSize(); + int expectedBuffLength = s.width * s.height * 3 /2; + if (expectedBuffLength != data.length) { + badBufferLengthReceived(data, expectedBuffLength); + return; + } + + long curTime = System.currentTimeMillis(); + if (lastFrameTime == 0) { + lastFrameTime = curTime; + putImage(filterCtxPtr, data); + return; + } + + double currentTimeElapsed = 0.8 * (curTime - lastFrameTime) / 1000 + 0.2 * timeElapsedBetweenFrames; + if (1 / currentTimeElapsed > fps) { +// Log.d("Linphone", "Clipping frame " + Math.round(1 / currentTimeElapsed) + " > " + fps); + addBackCaptureBuffer(data); + return; + } + lastFrameTime = curTime; + timeElapsedBetweenFrames = currentTimeElapsed; + + // Log.d("onPreviewFrame: ", Integer.toString(data.length)); putImage(filterCtxPtr, data); } + + // Hook + protected void badBufferLengthReceived(byte[] data, int expectedBuffLength) { + Log.e("Linphone", "onPreviewFrame called with bad buffer length " + data.length + + " whereas expected is " + expectedBuffLength + " don't calling putImage"); + } + } diff --git a/tutorials/JavaCameraRecordImpl.java b/tutorials/JavaCameraRecordImpl.java index 45499db78..30c1838f6 100644 --- a/tutorials/JavaCameraRecordImpl.java +++ b/tutorials/JavaCameraRecordImpl.java @@ -30,21 +30,16 @@ public class JavaCameraRecordImpl extends AndroidCameraRecord implements Preview private TextView debug; private long count = 0; private float averageCalledRate; - private int averageWindowSize = 2 * rate; private long startTime; private long endTime; - public JavaCameraRecordImpl(int rate) { - super(rate); - setPreviewCallBack(this); + public JavaCameraRecordImpl() { + super(); + setOrStorePreviewCallBack(this); } - public JavaCameraRecordImpl(int rate, int visilibity) { - super(rate, visilibity); - setPreviewCallBack(this); - } public void setDebug(TextView debug) { this.debug = debug; @@ -52,9 +47,9 @@ public class JavaCameraRecordImpl extends AndroidCameraRecord implements Preview public void onPreviewFrame(byte[] data, Camera camera) { - if ((count % averageWindowSize) == 0) { + if ((count % 2 * fps) == 0) { endTime = System.currentTimeMillis(); - averageCalledRate = (100000 * averageWindowSize) / (endTime - startTime); + averageCalledRate = (100000 * 2 * fps) / (endTime - startTime); averageCalledRate /= 100f; startTime = endTime; } diff --git a/tutorials/TestVideoActivity.java b/tutorials/TestVideoActivity.java index 6526e1cce..e016cad25 100644 --- a/tutorials/TestVideoActivity.java +++ b/tutorials/TestVideoActivity.java @@ -23,7 +23,7 @@ import org.linphone.core.AndroidCameraRecord; import android.app.Activity; import android.os.Bundle; -import android.view.SurfaceHolder; +import android.os.Handler; import android.view.SurfaceView; import android.widget.TextView; @@ -36,14 +36,11 @@ import android.widget.TextView; public class TestVideoActivity extends Activity { private SurfaceView surfaceView; - private static final int rate = 15; + private static final int rate = 7; - - - @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -51,14 +48,14 @@ public class TestVideoActivity extends Activity { surfaceView=(SurfaceView)findViewById(R.id.videotest_surfaceView); - SurfaceHolder holder=surfaceView.getHolder(); - holder.setFixedSize(320, 240); - holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); +// SurfaceHolder holder=surfaceView.getHolder(); +// holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); - AndroidCameraRecord.setSurfaceView(surfaceView); + AndroidCameraRecord.setSurfaceView(surfaceView, new Handler()); - JavaCameraRecordImpl manager = new JavaCameraRecordImpl(rate, SurfaceView.VISIBLE); - manager.setDebug((TextView) findViewById(R.id.videotest_debug)); + JavaCameraRecordImpl recorder = new JavaCameraRecordImpl(); + recorder.setDebug((TextView) findViewById(R.id.videotest_debug)); + recorder.setParameters(288, 352, rate, false); } From a79e8f7f9e79169bc8d15e953dfc1fc40df05d51 Mon Sep 17 00:00:00 2001 From: guillaume Date: Thu, 18 Nov 2010 15:01:15 +0100 Subject: [PATCH 10/38] addbackbuffer managed directly in java code --- AndroidCameraRecord.java | 8 -------- AndroidCameraRecordBufferedImpl.java | 11 +++-------- AndroidCameraRecordImpl.java | 9 ++------- 3 files changed, 5 insertions(+), 23 deletions(-) diff --git a/AndroidCameraRecord.java b/AndroidCameraRecord.java index 6ebce9856..17d9f8f9a 100644 --- a/AndroidCameraRecord.java +++ b/AndroidCameraRecord.java @@ -192,14 +192,6 @@ public abstract class AndroidCameraRecord { } - /** - * Hook to add back a buffer for reuse in capture. - * Override in a version supporting addPreviewCallBackWithBuffer() - * @param buffer buffer to reuse - */ - public void addBackCaptureBuffer(byte[] buffer) {} - - private void updateVisibility() { if (!visibilityChangeable) { diff --git a/AndroidCameraRecordBufferedImpl.java b/AndroidCameraRecordBufferedImpl.java index c65f479ad..715f17765 100644 --- a/AndroidCameraRecordBufferedImpl.java +++ b/AndroidCameraRecordBufferedImpl.java @@ -41,11 +41,6 @@ public class AndroidCameraRecordBufferedImpl extends AndroidCameraRecordImpl { camera.setPreviewCallbackWithBuffer(cb); } - @Override - public void addBackCaptureBuffer(byte[] buffer) { - camera.addCallbackBuffer(buffer); - } - @Override public void onCameraStarted(Camera camera) { super.onCameraStarted(camera); @@ -62,8 +57,8 @@ public class AndroidCameraRecordBufferedImpl extends AndroidCameraRecordImpl { } @Override - protected void badBufferLengthReceived(byte[] buffer, int expectedBufferLength) { - super.badBufferLengthReceived(buffer, expectedBufferLength); - addBackCaptureBuffer(buffer); + public void onPreviewFrame(byte[] data, Camera camera) { + super.onPreviewFrame(data, camera); + camera.addCallbackBuffer(data); } } diff --git a/AndroidCameraRecordImpl.java b/AndroidCameraRecordImpl.java index aab29a183..6186e458a 100644 --- a/AndroidCameraRecordImpl.java +++ b/AndroidCameraRecordImpl.java @@ -60,7 +60,8 @@ public class AndroidCameraRecordImpl extends AndroidCameraRecord implements Prev Size s = camera.getParameters().getPreviewSize(); int expectedBuffLength = s.width * s.height * 3 /2; if (expectedBuffLength != data.length) { - badBufferLengthReceived(data, expectedBuffLength); + Log.e("Linphone", "onPreviewFrame called with bad buffer length " + data.length + + " whereas expected is " + expectedBuffLength + " don't calling putImage"); return; } @@ -74,7 +75,6 @@ public class AndroidCameraRecordImpl extends AndroidCameraRecord implements Prev double currentTimeElapsed = 0.8 * (curTime - lastFrameTime) / 1000 + 0.2 * timeElapsedBetweenFrames; if (1 / currentTimeElapsed > fps) { // Log.d("Linphone", "Clipping frame " + Math.round(1 / currentTimeElapsed) + " > " + fps); - addBackCaptureBuffer(data); return; } lastFrameTime = curTime; @@ -85,10 +85,5 @@ public class AndroidCameraRecordImpl extends AndroidCameraRecord implements Prev } - // Hook - protected void badBufferLengthReceived(byte[] data, int expectedBuffLength) { - Log.e("Linphone", "onPreviewFrame called with bad buffer length " + data.length - + " whereas expected is " + expectedBuffLength + " don't calling putImage"); - } } From ae34456f2ad9ade1ed2922f42620132cb66e4d69 Mon Sep 17 00:00:00 2001 From: Simon Morlat Date: Fri, 19 Nov 2010 11:42:46 +0100 Subject: [PATCH 11/38] fix for display --- LinphoneCoreImpl.java | 1 - 1 file changed, 1 deletion(-) diff --git a/LinphoneCoreImpl.java b/LinphoneCoreImpl.java index c96373cb8..99be0fb2b 100644 --- a/LinphoneCoreImpl.java +++ b/LinphoneCoreImpl.java @@ -335,5 +335,4 @@ class LinphoneCoreImpl implements LinphoneCore { public boolean isVideoEnabled() { return isVideoEnabled(nativePtr); } - } From 7d71181e174154330812743542812e49c1db67f3 Mon Sep 17 00:00:00 2001 From: Simon Morlat Date: Fri, 19 Nov 2010 16:40:29 +0100 Subject: [PATCH 12/38] add stun preference --- LinphoneCoreImpl.java | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/LinphoneCoreImpl.java b/LinphoneCoreImpl.java index 99be0fb2b..1f389a608 100644 --- a/LinphoneCoreImpl.java +++ b/LinphoneCoreImpl.java @@ -69,12 +69,16 @@ class LinphoneCoreImpl implements LinphoneCore { private native void setPreviewWindowId(long nativePtr, Object wid); private AndroidVideoWindowImpl mVideoWindow; private AndroidVideoWindowImpl mPreviewWindow; - private native void addFriend(long nativePtr,long friend); private native void setPresenceInfo(long nativePtr,int minute_away, String alternative_contact,int status); private native long createChatRoom(long nativePtr,String to); private native void enableVideo(long nativePtr,boolean vcap_enabled,boolean display_enabled); private native boolean isVideoEnabled(long nativePtr); + private native void setFirewallPolicy(long nativePtr, int enum_value); + private native int getFirewallPolicy(long nativePtr); + private native void setStunServer(long nativePtr, String stun_server); + private native String getStunServer(long nativePtr); + private static String TAG = "LinphoneCore"; LinphoneCoreImpl(LinphoneCoreListener listener, File userConfig,File factoryConfig,Object userdata) throws IOException { @@ -335,4 +339,16 @@ class LinphoneCoreImpl implements LinphoneCore { public boolean isVideoEnabled() { return isVideoEnabled(nativePtr); } + public FirewallPolicy getFirewallPolicy() { + return FirewallPolicy.fromInt(getFirewallPolicy(nativePtr)); + } + public String getStunServer() { + return getStunServer(nativePtr); + } + public void setFirewallPolicy(FirewallPolicy pol) { + setFirewallPolicy(nativePtr,pol.value()); + } + public void setStunServer(String stunServer) { + setStunServer(nativePtr,stunServer); + } } From 2300dfa730ee2fbfb7b8407922445ad1df575174 Mon Sep 17 00:00:00 2001 From: Guillaume Beraudo Date: Mon, 22 Nov 2010 11:23:43 +0100 Subject: [PATCH 13/38] Added Orientation change support and video capture preview --- AndroidCameraRecord.java | 23 ++++++++++++++++++++--- AndroidCameraRecordImpl.java | 6 +++--- AndroidVideoWindowImpl.java | 5 ++++- 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/AndroidCameraRecord.java b/AndroidCameraRecord.java index 17d9f8f9a..df6a395ea 100644 --- a/AndroidCameraRecord.java +++ b/AndroidCameraRecord.java @@ -46,6 +46,7 @@ public abstract class AndroidCameraRecord { private static AndroidCameraRecord instance; private static Handler handler; private static boolean previewStarted; + private static int orientationCode; public AndroidCameraRecord() { // TODO check if another instance is loaded and kill it. @@ -66,6 +67,7 @@ public abstract class AndroidCameraRecord { } } + /* * AndroidCameraRecord.setSurfaceView() should be called first, from the Activity code. * It will start automatically @@ -97,9 +99,14 @@ public abstract class AndroidCameraRecord { parameters.setPreviewSize(width, height); parameters.setPreviewFrameRate(fps); +// parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_EDOF); camera.setParameters(parameters); + camera.setDisplayOrientation(90 * orientationCode); +// parameters.setRotation() + + SurfaceHolder holder = surfaceView.getHolder(); try { camera.setPreviewDisplay(holder); @@ -165,7 +172,7 @@ public abstract class AndroidCameraRecord { AndroidCameraRecord.surfaceView = null; if (camera == null) { - Log.e("AndroidCameraRecord.surfaceDestroyed", "illegal state"); + Log.e("Linphone", "Video capture: illegal state: surface destroyed but camera is already null"); return; } camera.setPreviewCallback(null); // TODO check if used whatever the SDK version @@ -173,20 +180,23 @@ public abstract class AndroidCameraRecord { camera.release(); camera=null; previewStarted = false; - Log.w("Linphone", "The video capture Surface view has been destroyed"); + Log.w("Linphone", "Video capture Surface destroyed"); } public void surfaceCreated(SurfaceHolder holder) { AndroidCameraRecord.surfaceView = sv; + Log.w("Linphone", "Video capture surface created"); if (instance != null) { instance.startPreview(); } + + holder.isCreating(); } public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { - // Do nothing + Log.w("Linphone", "Video capture surface changed"); } }); } @@ -223,7 +233,14 @@ public abstract class AndroidCameraRecord { protected void reallySetPreviewCallback(Camera camera, PreviewCallback cb) { camera.setPreviewCallback(cb); } + + public static void setOrientationCode(int orientation) { + AndroidCameraRecord.orientationCode = (4 + 1 - orientation) % 4; + } + protected int getOrientationCode() { + return orientationCode; + } } diff --git a/AndroidCameraRecordImpl.java b/AndroidCameraRecordImpl.java index 6186e458a..db4e128de 100644 --- a/AndroidCameraRecordImpl.java +++ b/AndroidCameraRecordImpl.java @@ -48,7 +48,7 @@ public class AndroidCameraRecordImpl extends AndroidCameraRecord implements Prev } - private native void putImage(long filterCtxPtr, byte[] buffer); + private native void putImage(long filterCtxPtr, byte[] buffer, int orientation); public void onPreviewFrame(byte[] data, Camera camera) { @@ -68,7 +68,7 @@ public class AndroidCameraRecordImpl extends AndroidCameraRecord implements Prev long curTime = System.currentTimeMillis(); if (lastFrameTime == 0) { lastFrameTime = curTime; - putImage(filterCtxPtr, data); + putImage(filterCtxPtr, data, getOrientationCode()); return; } @@ -81,7 +81,7 @@ public class AndroidCameraRecordImpl extends AndroidCameraRecord implements Prev timeElapsedBetweenFrames = currentTimeElapsed; // Log.d("onPreviewFrame: ", Integer.toString(data.length)); - putImage(filterCtxPtr, data); + putImage(filterCtxPtr, data, getOrientationCode()); } diff --git a/AndroidVideoWindowImpl.java b/AndroidVideoWindowImpl.java index efac2d826..ca9968e4c 100644 --- a/AndroidVideoWindowImpl.java +++ b/AndroidVideoWindowImpl.java @@ -34,9 +34,11 @@ public class AndroidVideoWindowImpl { mSurface=holder.getSurface(); } if (mListener!=null) mListener.onSurfaceReady(AndroidVideoWindowImpl.this); + Log.w("Linphone", "Video display surface changed"); } - public void surfaceCreated(SurfaceHolder holder) { + public void surfaceCreated(SurfaceHolder holder) { + Log.w("Linphone", "Video display surface created"); } public void surfaceDestroyed(SurfaceHolder holder) { @@ -46,6 +48,7 @@ public class AndroidVideoWindowImpl { } if (mListener!=null) mListener.onSurfaceDestroyed(AndroidVideoWindowImpl.this); + Log.w("Linphone", "Video display surface destroyed"); } }); } From 405d0b06ab9228789ea99f83e28854636c3ad051 Mon Sep 17 00:00:00 2001 From: Guillaume Beraudo Date: Mon, 22 Nov 2010 17:20:11 +0100 Subject: [PATCH 14/38] Added focus support. Rotation functinonal. --- AndroidCameraRecord.java | 25 ++++++++++++++++++++----- AndroidCameraRecordBufferedImpl.java | 10 ++++++++++ 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/AndroidCameraRecord.java b/AndroidCameraRecord.java index df6a395ea..4137192ba 100644 --- a/AndroidCameraRecord.java +++ b/AndroidCameraRecord.java @@ -20,6 +20,7 @@ package org.linphone.core; import android.hardware.Camera; import android.hardware.Camera.ErrorCallback; +import android.hardware.Camera.Parameters; import android.hardware.Camera.PreviewCallback; import android.os.Build; import android.os.Handler; @@ -46,7 +47,7 @@ public abstract class AndroidCameraRecord { private static AndroidCameraRecord instance; private static Handler handler; private static boolean previewStarted; - private static int orientationCode; + protected static int orientationCode; public AndroidCameraRecord() { // TODO check if another instance is loaded and kill it. @@ -99,11 +100,21 @@ public abstract class AndroidCameraRecord { parameters.setPreviewSize(width, height); parameters.setPreviewFrameRate(fps); -// parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_EDOF); - camera.setParameters(parameters); - camera.setDisplayOrientation(90 * orientationCode); -// parameters.setRotation() + if (parameters.getSupportedFocusModes().contains(Camera.Parameters.FOCUS_MODE_AUTO)) { + Log.w("Linphone", "Auto Focus supported by camera device"); + parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO); + } else { + Log.w("Linphone", "Auto Focus not supported by camera device"); + if (parameters.getSupportedFocusModes().contains(Camera.Parameters.FOCUS_MODE_INFINITY)) { + Log.w("Linphone", "Infinity Focus supported by camera device"); + parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_INFINITY); + } else { + Log.w("Linphone", "Infinity Focus not supported by camera device"); + } + } + onSettingParameters(parameters); + camera.setParameters(parameters); @@ -144,6 +155,10 @@ public abstract class AndroidCameraRecord { + protected void onSettingParameters(Parameters parameters) { + + } + /** * Hook. * @param camera diff --git a/AndroidCameraRecordBufferedImpl.java b/AndroidCameraRecordBufferedImpl.java index 715f17765..886ad222e 100644 --- a/AndroidCameraRecordBufferedImpl.java +++ b/AndroidCameraRecordBufferedImpl.java @@ -19,6 +19,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. package org.linphone.core; import android.hardware.Camera; +import android.hardware.Camera.Parameters; import android.hardware.Camera.PreviewCallback; import android.hardware.Camera.Size; import android.util.Log; @@ -61,4 +62,13 @@ public class AndroidCameraRecordBufferedImpl extends AndroidCameraRecordImpl { super.onPreviewFrame(data, camera); camera.addCallbackBuffer(data); } + + @Override + protected void onSettingParameters(Parameters parameters) { + super.onSettingParameters(parameters); + // Only on v8 hardware + camera.setDisplayOrientation(90 * orientationCode); + } + + } From c505024cc79e7e9fe902cd983462d3fb8b0e516a Mon Sep 17 00:00:00 2001 From: Guillaume Beraudo Date: Tue, 23 Nov 2010 09:27:51 +0100 Subject: [PATCH 15/38] Define getStatus(): throw not implemented exception --- LinphoneCallLogImpl.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/LinphoneCallLogImpl.java b/LinphoneCallLogImpl.java index b0360d194..1bdb84720 100644 --- a/LinphoneCallLogImpl.java +++ b/LinphoneCallLogImpl.java @@ -42,5 +42,8 @@ class LinphoneCallLogImpl implements LinphoneCallLog { public LinphoneAddress getTo() { return new LinphoneAddressImpl(getTo(nativePtr)); } + public CallStatus getStatus() { + throw new RuntimeException("not implemented yet"); + } } From dc20fb7739ea78b8ff90643ca053c770424d2e21 Mon Sep 17 00:00:00 2001 From: Guillaume Beraudo Date: Thu, 25 Nov 2010 11:13:28 +0100 Subject: [PATCH 16/38] Moved mic and speaker buttons in dialer. Added camera call button. Added video preferences. Share camera on received call according to preference settings. Invite with(out) according to preference settings. --- AndroidCameraRecord.java | 104 ++++++++++++++----------------- LinphoneCallImpl.java | 8 +++ LinphoneCallParamsImpl.java | 45 +++++++++++++ LinphoneCoreImpl.java | 32 +++++++++- tutorials/TestVideoActivity.java | 5 +- 5 files changed, 133 insertions(+), 61 deletions(-) create mode 100644 LinphoneCallParamsImpl.java diff --git a/AndroidCameraRecord.java b/AndroidCameraRecord.java index 4137192ba..c16ef065d 100644 --- a/AndroidCameraRecord.java +++ b/AndroidCameraRecord.java @@ -23,7 +23,6 @@ import android.hardware.Camera.ErrorCallback; import android.hardware.Camera.Parameters; import android.hardware.Camera.PreviewCallback; import android.os.Build; -import android.os.Handler; import android.util.Log; import android.view.SurfaceHolder; import android.view.SurfaceView; @@ -39,33 +38,26 @@ public abstract class AndroidCameraRecord { protected int fps; protected int height; protected int width; - private int longTermVisibility; - - private boolean visibilityChangeable = false; private PreviewCallback storedPreviewCallback; private static AndroidCameraRecord instance; - private static Handler handler; private static boolean previewStarted; + private static boolean parametersSet; protected static int orientationCode; + private static boolean mute; + private static final String tag="Linphone"; public AndroidCameraRecord() { // TODO check if another instance is loaded and kill it. instance = this; } - public void setParameters(int height, int width, float fps, boolean hide) { + public void setParameters(int height, int width, float fps) { this.fps = Math.round(fps); this.height = height; this.width = width; - this.longTermVisibility = hide ? SurfaceView.GONE : SurfaceView.VISIBLE; - - if (surfaceView != null) { - Log.d("Linphone", "Surfaceview defined and ready; starting video capture"); - instance.startPreview(); - } else { - Log.w("Linphone", "Surfaceview not defined; postponning video capture"); - } + parametersSet = true; + startPreview(); } @@ -74,16 +66,27 @@ public abstract class AndroidCameraRecord { * It will start automatically */ private void startPreview() { - assert surfaceView != null; + if (mute) { + Log.d(tag, "Not starting preview as camera has been muted"); + return; + } + if (surfaceView == null) { + Log.w(tag, "Surfaceview not defined; postponning video capture"); + return; + } + if (!parametersSet) { + Log.w(tag, "Parameters not set; postponning video capture"); + return; + } if (previewStarted) { - Log.w("Linphone", "Already started"); + Log.w(tag, "Already started"); return; } if (surfaceView.getVisibility() != SurfaceView.VISIBLE) { // Illegal state - Log.e("Linphone", "Illegal state: video capture surface view is not visible"); + Log.e(tag, "Illegal state: video capture surface view is not visible"); return; } @@ -91,7 +94,7 @@ public abstract class AndroidCameraRecord { camera=Camera.open(); camera.setErrorCallback(new ErrorCallback() { public void onError(int error, Camera camera) { - Log.e("Linphone", "Camera error : " + error); + Log.e(tag, "Camera error : " + error); } }); @@ -101,15 +104,15 @@ public abstract class AndroidCameraRecord { parameters.setPreviewSize(width, height); parameters.setPreviewFrameRate(fps); if (parameters.getSupportedFocusModes().contains(Camera.Parameters.FOCUS_MODE_AUTO)) { - Log.w("Linphone", "Auto Focus supported by camera device"); + Log.w(tag, "Auto Focus supported by camera device"); parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO); } else { - Log.w("Linphone", "Auto Focus not supported by camera device"); + Log.w(tag, "Auto Focus not supported by camera device"); if (parameters.getSupportedFocusModes().contains(Camera.Parameters.FOCUS_MODE_INFINITY)) { - Log.w("Linphone", "Infinity Focus supported by camera device"); + Log.w(tag, "Infinity Focus supported by camera device"); parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_INFINITY); } else { - Log.w("Linphone", "Infinity Focus not supported by camera device"); + Log.w(tag, "Infinity Focus not supported by camera device"); } } @@ -123,7 +126,7 @@ public abstract class AndroidCameraRecord { camera.setPreviewDisplay(holder); } catch (Throwable t) { - Log.e("Linphone", "Exception in Video capture setPreviewDisplay()", t); + Log.e(tag, "Exception in Video capture setPreviewDisplay()", t); } @@ -131,7 +134,7 @@ public abstract class AndroidCameraRecord { camera.startPreview(); previewStarted = true; } catch (Throwable e) { - Log.e("Linphone", "Can't start camera preview"); + Log.e(tag, "Can't start camera preview"); } previewStarted = true; @@ -143,11 +146,6 @@ public abstract class AndroidCameraRecord { reallySetPreviewCallback(camera, storedPreviewCallback); } - - visibilityChangeable = true; - if (surfaceView.getVisibility() != longTermVisibility) { - updateVisibility(); - } onCameraStarted(camera); } @@ -167,7 +165,7 @@ public abstract class AndroidCameraRecord { public void setOrStorePreviewCallBack(PreviewCallback cb) { if (camera == null) { - Log.w("Linphone", "Capture camera not ready, storing callback"); + Log.w(tag, "Capture camera not ready, storing callback"); this.storedPreviewCallback = cb; return; } @@ -177,8 +175,7 @@ public abstract class AndroidCameraRecord { - public static final void setSurfaceView(final SurfaceView sv, Handler mHandler) { - AndroidCameraRecord.handler = mHandler; + public static final void setSurfaceView(final SurfaceView sv) { SurfaceHolder holder = sv.getHolder(); holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); @@ -187,7 +184,7 @@ public abstract class AndroidCameraRecord { AndroidCameraRecord.surfaceView = null; if (camera == null) { - Log.e("Linphone", "Video capture: illegal state: surface destroyed but camera is already null"); + 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 @@ -195,12 +192,12 @@ public abstract class AndroidCameraRecord { camera.release(); camera=null; previewStarted = false; - Log.w("Linphone", "Video capture Surface destroyed"); + Log.w(tag, "Video capture Surface destroyed"); } public void surfaceCreated(SurfaceHolder holder) { AndroidCameraRecord.surfaceView = sv; - Log.w("Linphone", "Video capture surface created"); + Log.w(tag, "Video capture surface created"); if (instance != null) { instance.startPreview(); @@ -211,33 +208,13 @@ public abstract class AndroidCameraRecord { public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { - Log.w("Linphone", "Video capture surface changed"); + Log.w(tag, "Video capture surface changed"); } }); } - private void updateVisibility() { - if (!visibilityChangeable) { - throw new IllegalStateException("Visilibity not changeable now"); - } - - handler.post(new Runnable() { - public void run() { - Log.d("Linphone", "Changing video capture surface view visibility :" + longTermVisibility); - surfaceView.setVisibility(longTermVisibility); - } - }); - } - - public void setVisibility(int visibility) { - if (visibility == this.longTermVisibility) return; - - this.longTermVisibility = visibility; - updateVisibility(); - } - public void stopCaptureCallback() { if (camera != null) { @@ -256,6 +233,21 @@ public abstract class AndroidCameraRecord { protected int getOrientationCode() { return orientationCode; } + + public static void setMuteCamera(boolean m) { + if (m == mute) return; + + mute = m; + if (mute && previewStarted) { + camera.stopPreview(); + return; + } + + if (!mute) { + instance.startPreview(); + } + + } } diff --git a/LinphoneCallImpl.java b/LinphoneCallImpl.java index c71883b57..4c9a770c9 100644 --- a/LinphoneCallImpl.java +++ b/LinphoneCallImpl.java @@ -29,6 +29,8 @@ class LinphoneCallImpl implements LinphoneCall { private native boolean isIncoming(long nativePtr); native private long getRemoteAddress(long nativePtr); native private int getState(long nativePtr); + private native long getCurrentParams(long nativePtr); + protected LinphoneCallImpl(long aNativePtr) { nativePtr = aNativePtr; ref(nativePtr); @@ -58,6 +60,12 @@ class LinphoneCallImpl implements LinphoneCall { public State getState() { return LinphoneCall.State.fromInt(getState(nativePtr)); } + public LinphoneCallParams getCurrentParamsReadOnly() { + return new LinphoneCallParamsImpl(getCurrentParams(nativePtr)); + } + public LinphoneCallParams getCurrentParamsReadWrite() { + return getCurrentParamsReadOnly().copy(); + } } diff --git a/LinphoneCallParamsImpl.java b/LinphoneCallParamsImpl.java new file mode 100644 index 000000000..add724f21 --- /dev/null +++ b/LinphoneCallParamsImpl.java @@ -0,0 +1,45 @@ +/* +LinphoneCallParamsImpl.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.core; + +public class LinphoneCallParamsImpl implements LinphoneCallParams { + protected final long nativePtr; + + public LinphoneCallParamsImpl(long nativePtr) { + this.nativePtr = nativePtr; + } + + private native void enableVideo(long nativePtr, boolean b); + private native boolean getVideoEnabled(long nativePtr); + private native long copy(long nativePtr); + + + public boolean getVideoEnabled() { + return getVideoEnabled(nativePtr); + } + + public void setVideoEnalbled(boolean b) { + enableVideo(nativePtr, b); + } + + public LinphoneCallParams copy() { + return new LinphoneCallParamsImpl(copy(nativePtr)); + } + +} diff --git a/LinphoneCoreImpl.java b/LinphoneCoreImpl.java index 1f389a608..91913e322 100644 --- a/LinphoneCoreImpl.java +++ b/LinphoneCoreImpl.java @@ -55,6 +55,7 @@ class LinphoneCoreImpl implements LinphoneCore { private native void muteMic(long nativePtr,boolean isMuted); private native long interpretUrl(long nativePtr,String destination); private native long inviteAddress(long nativePtr,long to); + private native long inviteAddressWithParams(long nativePtrLc,long to, long nativePtrParam); private native void sendDtmf(long nativePtr,char dtmf); private native void clearCallLogs(long nativePtr); private native boolean isMicMuted(long nativePtr); @@ -78,6 +79,9 @@ class LinphoneCoreImpl implements LinphoneCore { private native int getFirewallPolicy(long nativePtr); private native void setStunServer(long nativePtr, String stun_server); private native String getStunServer(long nativePtr); + private native long createDefaultCallParams(long nativePtr); + private native int updateCall(long ptrLc, long ptrCall, long ptrParams); + private static String TAG = "LinphoneCore"; @@ -211,14 +215,15 @@ class LinphoneCoreImpl implements LinphoneCore { throw new LinphoneCoreException("Cannot interpret ["+destination+"]"); } } - public LinphoneCall invite(LinphoneAddress to) { + public LinphoneCall invite(LinphoneAddress to) throws LinphoneCoreException { long lNativePtr = inviteAddress(nativePtr,((LinphoneAddressImpl)to).nativePtr); if (lNativePtr!=0) { return new LinphoneCallImpl(lNativePtr); } else { - return null; + throw new LinphoneCoreException("Unable to invite address " + to.asString()); } } + public void sendDtmf(char number) { sendDtmf(nativePtr,number); } @@ -351,4 +356,27 @@ class LinphoneCoreImpl implements LinphoneCore { public void setStunServer(String stunServer) { setStunServer(nativePtr,stunServer); } + + public LinphoneCallParams createDefaultCallParameters() { + return new LinphoneCallParamsImpl(createDefaultCallParams(nativePtr)); + } + + public LinphoneCall inviteAddressWithParams(LinphoneAddress to, LinphoneCallParams params) throws LinphoneCoreException { + long ptrDestination = ((LinphoneAddressImpl)to).nativePtr; + long ptrParams =((LinphoneCallParamsImpl)params).nativePtr; + + long lcNativePtr = inviteAddressWithParams(nativePtr, ptrDestination, ptrParams); + if (lcNativePtr!=0) { + return new LinphoneCallImpl(lcNativePtr); + } else { + throw new LinphoneCoreException("Unable to invite with params " + to.asString()); + } + } + + public int updateCall(LinphoneCall call, LinphoneCallParams params) { + long ptrCall = ((LinphoneCallImpl) call).nativePtr; + long ptrParams = ((LinphoneCallParamsImpl)params).nativePtr; + + return updateCall(nativePtr, ptrCall, ptrParams); + } } diff --git a/tutorials/TestVideoActivity.java b/tutorials/TestVideoActivity.java index e016cad25..0623e34dc 100644 --- a/tutorials/TestVideoActivity.java +++ b/tutorials/TestVideoActivity.java @@ -23,7 +23,6 @@ import org.linphone.core.AndroidCameraRecord; import android.app.Activity; import android.os.Bundle; -import android.os.Handler; import android.view.SurfaceView; import android.widget.TextView; @@ -51,11 +50,11 @@ public class TestVideoActivity extends Activity { // SurfaceHolder holder=surfaceView.getHolder(); // holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); - AndroidCameraRecord.setSurfaceView(surfaceView, new Handler()); + AndroidCameraRecord.setSurfaceView(surfaceView); JavaCameraRecordImpl recorder = new JavaCameraRecordImpl(); recorder.setDebug((TextView) findViewById(R.id.videotest_debug)); - recorder.setParameters(288, 352, rate, false); + recorder.setParameters(288, 352, rate); } From 3c26f241c1ee443fc00be8aee388d1c0b88e2e5d Mon Sep 17 00:00:00 2001 From: Guillaume Beraudo Date: Fri, 26 Nov 2010 15:04:38 +0100 Subject: [PATCH 17/38] Added menu to videocall activity: - high/low resolution - enable/disable camera - return to dialer - terminate call Added bandwidth manager to manage video profiles and update current and subsequent calls. Added VideoSize semantics and jni wrappings on linphoneCore. --- AndroidCameraRecord.java | 65 +++++++++++++++++++++++++++++-------- LinphoneCallImpl.java | 2 -- LinphoneCallParamsImpl.java | 2 +- LinphoneCoreImpl.java | 28 +++++++++++++++- 4 files changed, 79 insertions(+), 18 deletions(-) diff --git a/AndroidCameraRecord.java b/AndroidCameraRecord.java index c16ef065d..138912f57 100644 --- a/AndroidCameraRecord.java +++ b/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/LinphoneCallImpl.java b/LinphoneCallImpl.java index 4c9a770c9..b6194c26f 100644 --- a/LinphoneCallImpl.java +++ b/LinphoneCallImpl.java @@ -66,6 +66,4 @@ class LinphoneCallImpl implements LinphoneCall { public LinphoneCallParams getCurrentParamsReadWrite() { return getCurrentParamsReadOnly().copy(); } - - } diff --git a/LinphoneCallParamsImpl.java b/LinphoneCallParamsImpl.java index add724f21..1242ee996 100644 --- a/LinphoneCallParamsImpl.java +++ b/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/LinphoneCoreImpl.java b/LinphoneCoreImpl.java index 91913e322..532a7ae2c 100644 --- a/LinphoneCoreImpl.java +++ b/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; + } } From a115f7a9d7b985c60858188875450d11a9ea1a1e Mon Sep 17 00:00:00 2001 From: Simon Morlat Date: Mon, 29 Nov 2010 10:53:03 +0100 Subject: [PATCH 18/38] fix bad merges and rename some methods --- LinphoneCoreImpl.java | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/LinphoneCoreImpl.java b/LinphoneCoreImpl.java index f0f4dc15d..c67c18dc9 100644 --- a/LinphoneCoreImpl.java +++ b/LinphoneCoreImpl.java @@ -385,9 +385,6 @@ class LinphoneCoreImpl implements LinphoneCore { return updateCall(nativePtr, ptrCall, ptrParams); } -<<<<<<< HEAD - - public void setUploadBandwidth(int bw) { setUploadBandwidth(nativePtr, bw); } @@ -406,14 +403,20 @@ class LinphoneCoreImpl implements LinphoneCore { VideoSize vSize = new VideoSize(); vSize.setWidth(nativeSize[0]); vSize.setHeight(nativeSize[1]); - return vSize; -======= + } public void setRing(String path) { setRing(nativePtr, path); } public String getRing() { return getRing(nativePtr); ->>>>>>> c95431295dc09c5558d38c6de49fa071c957d926 + } + public boolean isNetworkReachable() { + // TODO Auto-generated method stub + return false; + } + public void setNetworkReachable(boolean isReachable) { + // TODO Auto-generated method stub + } } From b091437c6f376a061b7994e0d98d9c311153113f Mon Sep 17 00:00:00 2001 From: Simon Morlat Date: Mon, 29 Nov 2010 12:01:44 +0100 Subject: [PATCH 19/38] fix setNetworkReachable --- AndroidCameraRecord.java | 8 +++++--- AndroidVideoWindowImpl.java | 1 + LinphoneCoreImpl.java | 6 +----- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/AndroidCameraRecord.java b/AndroidCameraRecord.java index 138912f57..eb6a10865 100644 --- a/AndroidCameraRecord.java +++ b/AndroidCameraRecord.java @@ -183,9 +183,11 @@ public abstract class AndroidCameraRecord { private static void stopPreview() { - camera.setPreviewCallback(null); // TODO check if used whatever the SDK version - camera.stopPreview(); - camera.release(); + if (camera!=null){ + camera.setPreviewCallback(null); // TODO check if used whatever the SDK version + camera.stopPreview(); + camera.release(); + } camera=null; previewStarted = false; } diff --git a/AndroidVideoWindowImpl.java b/AndroidVideoWindowImpl.java index ca9968e4c..9f6281bec 100644 --- a/AndroidVideoWindowImpl.java +++ b/AndroidVideoWindowImpl.java @@ -80,3 +80,4 @@ public class AndroidVideoWindowImpl { } } + diff --git a/LinphoneCoreImpl.java b/LinphoneCoreImpl.java index c67c18dc9..745c8c358 100644 --- a/LinphoneCoreImpl.java +++ b/LinphoneCoreImpl.java @@ -200,7 +200,7 @@ class LinphoneCoreImpl implements LinphoneCore { throw new RuntimeException("object already destroyed"); } } - public void setNetworkStateReachable(boolean isReachable) { + public void setNetworkReachable(boolean isReachable) { setNetworkStateReachable(nativePtr,isReachable); } public void setPlaybackGain(float gain) { @@ -415,8 +415,4 @@ class LinphoneCoreImpl implements LinphoneCore { // TODO Auto-generated method stub return false; } - public void setNetworkReachable(boolean isReachable) { - // TODO Auto-generated method stub - - } } From 3f7a0b217fc223fd3b09dd63098ecf367f632d3d Mon Sep 17 00:00:00 2001 From: Guillaume Beraudo Date: Mon, 29 Nov 2010 12:08:13 +0100 Subject: [PATCH 20/38] Added camera record manager to drive Video record class. --- AndroidCameraRecord.java | 175 +++++++----------------- AndroidCameraRecordBufferedImpl.java | 11 +- AndroidCameraRecordImpl.java | 27 ++-- AndroidCameraRecordManager.java | 190 +++++++++++++++++++++++++++ tutorials/JavaCameraRecordImpl.java | 16 ++- tutorials/TestVideoActivity.java | 9 +- 6 files changed, 277 insertions(+), 151 deletions(-) create mode 100644 AndroidCameraRecordManager.java diff --git a/AndroidCameraRecord.java b/AndroidCameraRecord.java index 138912f57..560f4c947 100644 --- a/AndroidCameraRecord.java +++ b/AndroidCameraRecord.java @@ -26,70 +26,36 @@ 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; import android.view.SurfaceView; -import android.view.SurfaceHolder.Callback; public abstract class AndroidCameraRecord { - public static final int ANDROID_VERSION = Integer.parseInt(Build.VERSION.SDK); - protected static Camera camera; - private static SurfaceView surfaceView; + protected Camera camera; + private RecorderParams params; - protected int fps; - protected int height; - protected int width; private PreviewCallback storedPreviewCallback; - - private static AndroidCameraRecord instance; - private static boolean previewStarted; - private static boolean parametersSet; - protected static int orientationCode; - private static boolean muted; + private boolean previewStarted; + protected int orientationCode; private static final String tag="Linphone"; - private static List supportedVideoSizes; + private List supportedVideoSizes; - public AndroidCameraRecord() { - // TODO check if another instance is loaded and kill it. - instance = this; - } - - public void setParameters(int height, int width, float fps) { - this.fps = Math.round(fps); - this.height = height; - this.width = width; - parametersSet = true; - startPreview(); + public AndroidCameraRecord(RecorderParams parameters) { + this.params = parameters; + setRotation(parameters.rotation); } - /* - * AndroidCameraRecord.setSurfaceView() should be called first, from the Activity code. - * It will start automatically - */ - private void startPreview() { - if (muted) { - Log.d(tag, "Not starting preview as camera has been muted"); - return; - } - if (surfaceView == null) { - Log.w(tag, "Surfaceview not defined; postponning video capture"); - return; - } - if (!parametersSet) { - Log.w(tag, "Parameters not set; postponning video capture"); - return; - } - + + public void startPreview() { // FIXME throws exception? if (previewStarted) { Log.w(tag, "Already started"); return; } - if (surfaceView.getVisibility() != SurfaceView.VISIBLE) { + if (params.surfaceView.getVisibility() != SurfaceView.VISIBLE) { // Illegal state Log.e(tag, "Illegal state: video capture surface view is not visible"); return; @@ -109,8 +75,9 @@ public abstract class AndroidCameraRecord { supportedVideoSizes = camera.getParameters().getSupportedPreviewSizes(); } - parameters.setPreviewSize(width, height); - parameters.setPreviewFrameRate(fps); + parameters.set("camera-id", params.cameraId); + parameters.setPreviewSize(params.width, params.height); + parameters.setPreviewFrameRate(Math.round(params.fps)); if (parameters.getSupportedFocusModes().contains(Camera.Parameters.FOCUS_MODE_AUTO)) { Log.w(tag, "Auto Focus supported by camera device"); parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO); @@ -127,9 +94,8 @@ public abstract class AndroidCameraRecord { onSettingParameters(parameters); camera.setParameters(parameters); - - SurfaceHolder holder = surfaceView.getHolder(); + SurfaceHolder holder = params.surfaceView.getHolder(); try { camera.setPreviewDisplay(holder); } @@ -151,7 +117,7 @@ public abstract class AndroidCameraRecord { // Register callback to get capture buffer if (storedPreviewCallback != null) { - reallySetPreviewCallback(camera, storedPreviewCallback); + lowLevelSetPreviewCallback(camera, storedPreviewCallback); } @@ -171,120 +137,69 @@ public abstract class AndroidCameraRecord { */ public void onCameraStarted(Camera camera) {} - public void setOrStorePreviewCallBack(PreviewCallback cb) { + public void storePreviewCallBack(PreviewCallback cb) { if (camera == null) { Log.w(tag, "Capture camera not ready, storing callback"); this.storedPreviewCallback = cb; return; } - reallySetPreviewCallback(camera, cb); + lowLevelSetPreviewCallback(camera, cb); } - private static void stopPreview() { - camera.setPreviewCallback(null); // TODO check if used whatever the SDK version + void stopPreview() { + if (!previewStarted) return; + lowLevelSetPreviewCallback(camera, null); camera.stopPreview(); camera.release(); camera=null; previewStarted = false; } - public static final void setSurfaceView(final SurfaceView sv) { - SurfaceHolder holder = sv.getHolder(); - holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); - - holder.addCallback(new Callback() { - public void surfaceDestroyed(SurfaceHolder holder) { - AndroidCameraRecord.surfaceView = null; - - if (camera == null) { - Log.e(tag, "Video capture: illegal state: surface destroyed but camera is already null"); - return; - } - stopPreview(); - Log.w(tag, "Video capture Surface destroyed"); - } - - public void surfaceCreated(SurfaceHolder holder) { - AndroidCameraRecord.surfaceView = sv; - Log.w(tag, "Video capture surface created"); - - if (instance != null) { - instance.startPreview(); - } - - holder.isCreating(); - } - - public void surfaceChanged(SurfaceHolder holder, int format, int width, - int height) { - Log.w(tag, "Video capture surface changed"); - } - }); - } - - - public void stopCaptureCallback() { if (camera != null) { - reallySetPreviewCallback(camera, null); + lowLevelSetPreviewCallback(camera, null); } } - protected void reallySetPreviewCallback(Camera camera, PreviewCallback cb) { - camera.setPreviewCallback(cb); - } + protected abstract void lowLevelSetPreviewCallback(Camera camera, PreviewCallback cb); - public static void setOrientationCode(int orientation) { - AndroidCameraRecord.orientationCode = (4 + 1 - orientation) % 4; + public void setRotation(int rotation) { + orientationCode = (4 + 1 - rotation) % 4; } protected int getOrientationCode() { return orientationCode; } - public static void setMuteCamera(boolean m) { - if (m == muted) return; - muted = m; - if (muted && previewStarted) { - stopPreview(); - return; - } + + + public static class RecorderParams { + public float fps; + public int height; + public int width; + + final long filterDataNativePtr; + int cameraId; + int rotation; + public SurfaceView surfaceView; - if (!muted) { - instance.startPreview(); + public RecorderParams(long ptr) { + filterDataNativePtr = ptr; } - } - public static void toggleMute() { - setMuteCamera(!muted); + + + + public boolean isStarted() { + return previewStarted; } - 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(); + public List getSupportedVideoSizes() { + return new ArrayList(supportedVideoSizes); } } diff --git a/AndroidCameraRecordBufferedImpl.java b/AndroidCameraRecordBufferedImpl.java index 886ad222e..9445b40ee 100644 --- a/AndroidCameraRecordBufferedImpl.java +++ b/AndroidCameraRecordBufferedImpl.java @@ -32,13 +32,16 @@ import android.util.Log; */ public class AndroidCameraRecordBufferedImpl extends AndroidCameraRecordImpl { - public AndroidCameraRecordBufferedImpl(long filterCtxPtr) { - super(filterCtxPtr); + + public AndroidCameraRecordBufferedImpl(RecorderParams parameters) { + super(parameters); } @Override - protected void reallySetPreviewCallback(Camera camera, PreviewCallback cb) { - Log.d("Linphone", "Setting optimized callback with buffer (Android >= 8). Remember to manage the pool of buffers!!!"); + protected void lowLevelSetPreviewCallback(Camera camera, PreviewCallback cb) { + if (cb != null) { + Log.d("Linphone", "Setting optimized callback with buffer (Android >= 8). Remember to manage the pool of buffers!!!"); + } camera.setPreviewCallbackWithBuffer(cb); } diff --git a/AndroidCameraRecordImpl.java b/AndroidCameraRecordImpl.java index db4e128de..38ca02fec 100644 --- a/AndroidCameraRecordImpl.java +++ b/AndroidCameraRecordImpl.java @@ -34,17 +34,14 @@ public class AndroidCameraRecordImpl extends AndroidCameraRecord implements Prev private long filterCtxPtr; private double timeElapsedBetweenFrames = 0; private long lastFrameTime = 0; + private final long expectedTimeBetweenFrames; - public AndroidCameraRecordImpl(long filterCtxPtr) { - super(); + public AndroidCameraRecordImpl(RecorderParams parameters) { + super(parameters); + expectedTimeBetweenFrames = 1l / Math.round(parameters.fps); + filterCtxPtr = parameters.filterDataNativePtr; - try { - this.filterCtxPtr = filterCtxPtr; - setOrStorePreviewCallBack(this); - } catch (Throwable e) { - Log.e("Linphone", "Error"); - } - + storePreviewCallBack(this); } @@ -56,6 +53,10 @@ public class AndroidCameraRecordImpl extends AndroidCameraRecord implements Prev Log.e("Linphone", "onPreviewFrame Called with null buffer"); return; } + if (filterCtxPtr == 0l) { + Log.e("Linphone", "onPreviewFrame Called with no filterCtxPtr set"); + return; + } Size s = camera.getParameters().getPreviewSize(); int expectedBuffLength = s.width * s.height * 3 /2; @@ -73,7 +74,7 @@ public class AndroidCameraRecordImpl extends AndroidCameraRecord implements Prev } double currentTimeElapsed = 0.8 * (curTime - lastFrameTime) / 1000 + 0.2 * timeElapsedBetweenFrames; - if (1 / currentTimeElapsed > fps) { + if (currentTimeElapsed < expectedTimeBetweenFrames) { // Log.d("Linphone", "Clipping frame " + Math.round(1 / currentTimeElapsed) + " > " + fps); return; } @@ -85,5 +86,11 @@ public class AndroidCameraRecordImpl extends AndroidCameraRecord implements Prev } + @Override + protected void lowLevelSetPreviewCallback(Camera camera, PreviewCallback cb) { + camera.setPreviewCallback(cb); + } + + } diff --git a/AndroidCameraRecordManager.java b/AndroidCameraRecordManager.java new file mode 100644 index 000000000..9295bf0b0 --- /dev/null +++ b/AndroidCameraRecordManager.java @@ -0,0 +1,190 @@ +/* +AndroidCameraRecordManager.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.core; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.linphone.core.AndroidCameraRecord.RecorderParams; + +import android.hardware.Camera; +import android.hardware.Camera.Size; +import android.os.Build; +import android.util.Log; +import android.view.SurfaceHolder; +import android.view.SurfaceView; +import android.view.SurfaceHolder.Callback; + + + +/** + * Manage the video capture; one instance per camera. + * + * @author Guillaume Beraudo + * + */ +public class AndroidCameraRecordManager { + public static final int CAMERA_ID_FIXME_USE_PREFERENCE = 0; + private static final int version = Integer.parseInt(Build.VERSION.SDK); + private static Map instances = new HashMap(); + + + // singleton + private AndroidCameraRecordManager(int cameraId) { + this.cameraId = cameraId; + } + + /** + * @param cameraId : see max_camera_id + * @return + */ + public static final synchronized AndroidCameraRecordManager getInstance(int cameraId) { + if (cameraId < 0) { + Log.e("Linphone", "Asking unmanageable camera " + cameraId); + return null; + } + + AndroidCameraRecordManager m = instances.get(cameraId); + if (m == null) { + m = new AndroidCameraRecordManager(cameraId); + instances.put(cameraId, m); + } + return m; + } + + public static final synchronized AndroidCameraRecordManager getInstance() { + return getInstance(0); + } + + private AndroidCameraRecord.RecorderParams parameters; + private SurfaceView surfaceView; + private boolean muted; + + + private AndroidCameraRecord recorder; + private final Integer cameraId; + + private List supportedVideoSizes; + private int rotation; + + + public void setParametersFromFilter(long filterDataPtr, int height, int width, float fps) { + RecorderParams p = new RecorderParams(filterDataPtr); + p.fps = fps; + p.width = width; + p.height = height; + p.cameraId = cameraId; + parameters = p; + } + + + public final void setSurfaceView(final SurfaceView sv, final int rotation) { + this.rotation = rotation; + SurfaceHolder holder = sv.getHolder(); + holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); + + holder.addCallback(new Callback() { + public void surfaceDestroyed(SurfaceHolder holder) { + surfaceView = null; + stopVideoRecording(); + } + + public void surfaceCreated(SurfaceHolder holder) { + surfaceView = sv; + tryToStartVideoRecording(); + } + + public void surfaceChanged(SurfaceHolder holder, int format, int width, + int height) {} + }); + } + + public void setMuted(boolean muteState) { + if (muteState == muted) return; + muted = muteState; + if (muted) { + stopVideoRecording(); + } else { + tryToStartVideoRecording(); + } + } + public void toggleMute() { + setMuted(!muted); + } + public boolean isMuted() { + return muted; + } + + + private void tryToStartVideoRecording() { + if (muted || surfaceView == null || parameters == null) return; + + parameters.rotation = rotation; + parameters.surfaceView = surfaceView; + if (version > 8) { + recorder = new AndroidCameraRecordBufferedImpl(parameters); + } else { + recorder = new AndroidCameraRecordImpl(parameters); + } + + recorder.startPreview(); + } + + public void stopVideoRecording() { + if (recorder != null) { + recorder.stopPreview(); + recorder = null; + } + } + + + // FIXME select right camera + public List supportedVideoSizes() { + if (supportedVideoSizes != null) { + return supportedVideoSizes; + } + + if (recorder != null) { + supportedVideoSizes = recorder.getSupportedVideoSizes(); + if (supportedVideoSizes != null) return supportedVideoSizes; + } + + Camera camera = Camera.open(); + supportedVideoSizes = camera.getParameters().getSupportedPreviewSizes(); + camera.release(); + return supportedVideoSizes; + } + + + public boolean isRecording() { + if (recorder != null) { + return recorder.isStarted(); + } + + return false; + } + + public void invalidateParameters() { + if (isRecording()) stopVideoRecording(); + parameters = null; + + } + +} diff --git a/tutorials/JavaCameraRecordImpl.java b/tutorials/JavaCameraRecordImpl.java index 30c1838f6..12594ccef 100644 --- a/tutorials/JavaCameraRecordImpl.java +++ b/tutorials/JavaCameraRecordImpl.java @@ -33,11 +33,13 @@ public class JavaCameraRecordImpl extends AndroidCameraRecord implements Preview private long startTime; private long endTime; + private int fps; - - public JavaCameraRecordImpl() { - super(); - setOrStorePreviewCallBack(this); + + public JavaCameraRecordImpl(AndroidCameraRecord.RecorderParams parameters) { + super(parameters); + storePreviewCallBack(this); + fps = Math.round(parameters.fps); } @@ -61,4 +63,10 @@ public class JavaCameraRecordImpl extends AndroidCameraRecord implements Preview Log.d("onPreviewFrame:", msg); } + + @Override + protected void lowLevelSetPreviewCallback(Camera camera, PreviewCallback cb) { + camera.setPreviewCallback(cb); + } + } diff --git a/tutorials/TestVideoActivity.java b/tutorials/TestVideoActivity.java index 0623e34dc..4b4ccc09e 100644 --- a/tutorials/TestVideoActivity.java +++ b/tutorials/TestVideoActivity.java @@ -50,11 +50,14 @@ public class TestVideoActivity extends Activity { // SurfaceHolder holder=surfaceView.getHolder(); // holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); - AndroidCameraRecord.setSurfaceView(surfaceView); + AndroidCameraRecord.RecorderParams params = new AndroidCameraRecord.RecorderParams(0); + params.surfaceView = surfaceView; + params.width = 352; + params.height = 288; + params.fps = rate; - JavaCameraRecordImpl recorder = new JavaCameraRecordImpl(); + JavaCameraRecordImpl recorder = new JavaCameraRecordImpl(params); recorder.setDebug((TextView) findViewById(R.id.videotest_debug)); - recorder.setParameters(288, 352, rate); } From 944aa6f02857bf93811ea3f13bd23d95e98c4c0b Mon Sep 17 00:00:00 2001 From: Guillaume Beraudo Date: Mon, 29 Nov 2010 14:37:42 +0100 Subject: [PATCH 21/38] Fix regression framerate control. --- AndroidCameraRecordImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AndroidCameraRecordImpl.java b/AndroidCameraRecordImpl.java index 38ca02fec..0409d5cb4 100644 --- a/AndroidCameraRecordImpl.java +++ b/AndroidCameraRecordImpl.java @@ -34,11 +34,11 @@ public class AndroidCameraRecordImpl extends AndroidCameraRecord implements Prev private long filterCtxPtr; private double timeElapsedBetweenFrames = 0; private long lastFrameTime = 0; - private final long expectedTimeBetweenFrames; + private final double expectedTimeBetweenFrames; public AndroidCameraRecordImpl(RecorderParams parameters) { super(parameters); - expectedTimeBetweenFrames = 1l / Math.round(parameters.fps); + expectedTimeBetweenFrames = 1d / Math.round(parameters.fps); filterCtxPtr = parameters.filterDataNativePtr; storePreviewCallBack(this); From 7b9233ddf839fa712c2b706837d19ae5c9cbe59c Mon Sep 17 00:00:00 2001 From: Guillaume Beraudo Date: Mon, 29 Nov 2010 14:40:00 +0100 Subject: [PATCH 22/38] Fix regression not using buffered capture on android v8. --- AndroidCameraRecordManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AndroidCameraRecordManager.java b/AndroidCameraRecordManager.java index 9295bf0b0..159cc4c5a 100644 --- a/AndroidCameraRecordManager.java +++ b/AndroidCameraRecordManager.java @@ -138,7 +138,7 @@ public class AndroidCameraRecordManager { parameters.rotation = rotation; parameters.surfaceView = surfaceView; - if (version > 8) { + if (version >= 8) { recorder = new AndroidCameraRecordBufferedImpl(parameters); } else { recorder = new AndroidCameraRecordImpl(parameters); From 6c9b1ab07933197e91098e8c316b962e5edec2e0 Mon Sep 17 00:00:00 2001 From: Guillaume Beraudo Date: Mon, 29 Nov 2010 14:41:51 +0100 Subject: [PATCH 23/38] Cosmetics cleanup. --- AndroidCameraRecord.java | 20 ++++++++------------ AndroidCameraRecordBufferedImpl.java | 12 ++++-------- 2 files changed, 12 insertions(+), 20 deletions(-) diff --git a/AndroidCameraRecord.java b/AndroidCameraRecord.java index 560f4c947..c691a3176 100644 --- a/AndroidCameraRecord.java +++ b/AndroidCameraRecord.java @@ -52,7 +52,8 @@ public abstract class AndroidCameraRecord { public void startPreview() { // FIXME throws exception? if (previewStarted) { Log.w(tag, "Already started"); - return; + throw new RuntimeException("Video recorder already started"); + // return } if (params.surfaceView.getVisibility() != SurfaceView.VISIBLE) { @@ -91,7 +92,7 @@ public abstract class AndroidCameraRecord { } } - onSettingParameters(parameters); + onSettingCameraParameters(parameters); camera.setParameters(parameters); @@ -114,33 +115,28 @@ public abstract class AndroidCameraRecord { previewStarted = true; - // Register callback to get capture buffer - if (storedPreviewCallback != null) { - lowLevelSetPreviewCallback(camera, storedPreviewCallback); - } + lowLevelSetPreviewCallback(camera, storedPreviewCallback); - onCameraStarted(camera); + onPreviewStarted(camera); } - protected void onSettingParameters(Parameters parameters) { - - } + protected void onSettingCameraParameters(Parameters parameters) {} /** * Hook. * @param camera */ - public void onCameraStarted(Camera camera) {} + public void onPreviewStarted(Camera camera) {} public void storePreviewCallBack(PreviewCallback cb) { + this.storedPreviewCallback = cb; if (camera == null) { Log.w(tag, "Capture camera not ready, storing callback"); - this.storedPreviewCallback = cb; return; } diff --git a/AndroidCameraRecordBufferedImpl.java b/AndroidCameraRecordBufferedImpl.java index 9445b40ee..f58698df2 100644 --- a/AndroidCameraRecordBufferedImpl.java +++ b/AndroidCameraRecordBufferedImpl.java @@ -46,18 +46,14 @@ public class AndroidCameraRecordBufferedImpl extends AndroidCameraRecordImpl { } @Override - public void onCameraStarted(Camera camera) { - super.onCameraStarted(camera); + public void onPreviewStarted(Camera camera) { + super.onPreviewStarted(camera); Size s = camera.getParameters().getPreviewSize(); int wishedBufferSize = s.height * s.width * 3 / 2; camera.addCallbackBuffer(new byte[wishedBufferSize]); camera.addCallbackBuffer(new byte[wishedBufferSize]); -/* - for (int i=1; i < 30; i++) { - camera.addCallbackBuffer(new byte[wishedBufferSize]); - }*/ } @Override @@ -67,8 +63,8 @@ public class AndroidCameraRecordBufferedImpl extends AndroidCameraRecordImpl { } @Override - protected void onSettingParameters(Parameters parameters) { - super.onSettingParameters(parameters); + protected void onSettingCameraParameters(Parameters parameters) { + super.onSettingCameraParameters(parameters); // Only on v8 hardware camera.setDisplayOrientation(90 * orientationCode); } From 0e3fb02e229115fe62ec7d40ee38a8eab483a832 Mon Sep 17 00:00:00 2001 From: Simon Morlat Date: Mon, 29 Nov 2010 15:06:17 +0100 Subject: [PATCH 24/38] fixed orientation doesn't work --- AndroidVideoWindowImpl.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/AndroidVideoWindowImpl.java b/AndroidVideoWindowImpl.java index 9f6281bec..ec7eb2cbb 100644 --- a/AndroidVideoWindowImpl.java +++ b/AndroidVideoWindowImpl.java @@ -48,10 +48,16 @@ public class AndroidVideoWindowImpl { } if (mListener!=null) mListener.onSurfaceDestroyed(AndroidVideoWindowImpl.this); - Log.w("Linphone", "Video display surface destroyed"); + Log.d("Linphone", "Video display surface destroyed"); } }); } + static final int LANDSCAPE=0; + static final int PORTRAIT=1; + public void requestOrientation(int orientation){ + //Surface.setOrientation(0, orientation==LANDSCAPE ? 1 : 0); + //Log.d("Linphone", "Orientation changed."); + } public void setListener(VideoWindowListener l){ mListener=l; } From 8f406c57066cf0e8b002d5f46c5488030e0d0166 Mon Sep 17 00:00:00 2001 From: Guillaume Beraudo Date: Wed, 1 Dec 2010 11:39:19 +0100 Subject: [PATCH 25/38] Portrait mode. --- AndroidCameraRecord.java | 7 ++++++- AndroidCameraRecordImpl.java | 8 +++++--- AndroidCameraRecordManager.java | 13 ++++++++++--- LinphoneCallImpl.java | 5 +++++ 4 files changed, 26 insertions(+), 7 deletions(-) diff --git a/AndroidCameraRecord.java b/AndroidCameraRecord.java index c691a3176..9d8b2f144 100644 --- a/AndroidCameraRecord.java +++ b/AndroidCameraRecord.java @@ -77,7 +77,11 @@ public abstract class AndroidCameraRecord { } parameters.set("camera-id", params.cameraId); - parameters.setPreviewSize(params.width, params.height); + if (!params.videoDimensionsInverted) { + parameters.setPreviewSize(params.width, params.height); + } else { + parameters.setPreviewSize(params.height, params.width); + } parameters.setPreviewFrameRate(Math.round(params.fps)); if (parameters.getSupportedFocusModes().contains(Camera.Parameters.FOCUS_MODE_AUTO)) { Log.w(tag, "Auto Focus supported by camera device"); @@ -182,6 +186,7 @@ public abstract class AndroidCameraRecord { int cameraId; int rotation; public SurfaceView surfaceView; + boolean videoDimensionsInverted; public RecorderParams(long ptr) { filterDataNativePtr = ptr; diff --git a/AndroidCameraRecordImpl.java b/AndroidCameraRecordImpl.java index 0409d5cb4..2d3087854 100644 --- a/AndroidCameraRecordImpl.java +++ b/AndroidCameraRecordImpl.java @@ -35,17 +35,19 @@ public class AndroidCameraRecordImpl extends AndroidCameraRecord implements Prev private double timeElapsedBetweenFrames = 0; private long lastFrameTime = 0; private final double expectedTimeBetweenFrames; + private boolean videoDimensionsInverted; public AndroidCameraRecordImpl(RecorderParams parameters) { super(parameters); expectedTimeBetweenFrames = 1d / Math.round(parameters.fps); filterCtxPtr = parameters.filterDataNativePtr; + videoDimensionsInverted = parameters.videoDimensionsInverted; storePreviewCallBack(this); } - private native void putImage(long filterCtxPtr, byte[] buffer, int orientation); + private native void putImage(long filterCtxPtr, byte[] buffer, int orientation, boolean videoDimensionsInverted); public void onPreviewFrame(byte[] data, Camera camera) { @@ -69,7 +71,7 @@ public class AndroidCameraRecordImpl extends AndroidCameraRecord implements Prev long curTime = System.currentTimeMillis(); if (lastFrameTime == 0) { lastFrameTime = curTime; - putImage(filterCtxPtr, data, getOrientationCode()); + putImage(filterCtxPtr, data, getOrientationCode(), videoDimensionsInverted); return; } @@ -82,7 +84,7 @@ public class AndroidCameraRecordImpl extends AndroidCameraRecord implements Prev timeElapsedBetweenFrames = currentTimeElapsed; // Log.d("onPreviewFrame: ", Integer.toString(data.length)); - putImage(filterCtxPtr, data, getOrientationCode()); + putImage(filterCtxPtr, data, getOrientationCode(), videoDimensionsInverted); } diff --git a/AndroidCameraRecordManager.java b/AndroidCameraRecordManager.java index 159cc4c5a..02e7eb497 100644 --- a/AndroidCameraRecordManager.java +++ b/AndroidCameraRecordManager.java @@ -44,7 +44,7 @@ public class AndroidCameraRecordManager { public static final int CAMERA_ID_FIXME_USE_PREFERENCE = 0; private static final int version = Integer.parseInt(Build.VERSION.SDK); private static Map instances = new HashMap(); - + // singleton private AndroidCameraRecordManager(int cameraId) { @@ -83,16 +83,19 @@ public class AndroidCameraRecordManager { private List supportedVideoSizes; private int rotation; + private static final String tag = "Linphone"; public void setParametersFromFilter(long filterDataPtr, int height, int width, float fps) { + stopVideoRecording(); RecorderParams p = new RecorderParams(filterDataPtr); p.fps = fps; p.width = width; p.height = height; p.cameraId = cameraId; + p.videoDimensionsInverted = width < height; parameters = p; - } + } public final void setSurfaceView(final SurfaceView sv, final int rotation) { @@ -103,16 +106,20 @@ public class AndroidCameraRecordManager { holder.addCallback(new Callback() { public void surfaceDestroyed(SurfaceHolder holder) { surfaceView = null; + Log.d(tag , "Video capture surface destroyed"); stopVideoRecording(); } public void surfaceCreated(SurfaceHolder holder) { surfaceView = sv; + Log.d(tag , "Video capture surface created"); tryToStartVideoRecording(); } public void surfaceChanged(SurfaceHolder holder, int format, int width, - int height) {} + int height) { + Log.d(tag , "Video capture surface changed"); + } }); } diff --git a/LinphoneCallImpl.java b/LinphoneCallImpl.java index b6194c26f..a73492274 100644 --- a/LinphoneCallImpl.java +++ b/LinphoneCallImpl.java @@ -30,6 +30,7 @@ class LinphoneCallImpl implements LinphoneCall { native private long getRemoteAddress(long nativePtr); native private int getState(long nativePtr); private native long getCurrentParams(long nativePtr); + private native void enableCamera(long nativePtr, boolean enabled); protected LinphoneCallImpl(long aNativePtr) { nativePtr = aNativePtr; @@ -66,4 +67,8 @@ class LinphoneCallImpl implements LinphoneCall { public LinphoneCallParams getCurrentParamsReadWrite() { return getCurrentParamsReadOnly().copy(); } + + public void enableCamera(boolean enabled) { + enableCamera(nativePtr, enabled); + } } From 8b7584e4c644d8adde23fbfe711a911e4a711374 Mon Sep 17 00:00:00 2001 From: Guillaume Beraudo Date: Wed, 1 Dec 2010 14:24:39 +0100 Subject: [PATCH 26/38] Fix video capture not restarting on setParameters from filter. --- AndroidCameraRecordManager.java | 1 + 1 file changed, 1 insertion(+) diff --git a/AndroidCameraRecordManager.java b/AndroidCameraRecordManager.java index 02e7eb497..800879dd5 100644 --- a/AndroidCameraRecordManager.java +++ b/AndroidCameraRecordManager.java @@ -95,6 +95,7 @@ public class AndroidCameraRecordManager { p.cameraId = cameraId; p.videoDimensionsInverted = width < height; parameters = p; + tryToStartVideoRecording(); } From bf7f4a028643204629c9526cb0efe8336791fa79 Mon Sep 17 00:00:00 2001 From: Guillaume Beraudo Date: Wed, 1 Dec 2010 17:03:22 +0100 Subject: [PATCH 27/38] Cleanup. --- AndroidCameraRecordManager.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AndroidCameraRecordManager.java b/AndroidCameraRecordManager.java index 800879dd5..4c767b887 100644 --- a/AndroidCameraRecordManager.java +++ b/AndroidCameraRecordManager.java @@ -189,10 +189,10 @@ public class AndroidCameraRecordManager { return false; } + public void invalidateParameters() { - if (isRecording()) stopVideoRecording(); + stopVideoRecording(); parameters = null; - } } From fb6a4a0bb508e66439f194e9a0d538f7f6ce8fc2 Mon Sep 17 00:00:00 2001 From: Guillaume Beraudo Date: Thu, 2 Dec 2010 12:29:51 +0100 Subject: [PATCH 28/38] Fix links to ABI>=5 in video code preventing use with old telephones. Disable video when ABI<5 or ILBC not found. Automatically open preference activity on linphone first launch. --- AndroidCameraRecord.java | 22 +++------- AndroidCameraRecordBufferedImpl.java | 2 +- AndroidCameraRecordImplAPI5.java | 64 ++++++++++++++++++++++++++++ AndroidCameraRecordManager.java | 23 +++++++--- 4 files changed, 89 insertions(+), 22 deletions(-) create mode 100644 AndroidCameraRecordImplAPI5.java diff --git a/AndroidCameraRecord.java b/AndroidCameraRecord.java index 9d8b2f144..7fa4746bb 100644 --- a/AndroidCameraRecord.java +++ b/AndroidCameraRecord.java @@ -19,6 +19,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. package org.linphone.core; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import android.hardware.Camera; @@ -39,7 +40,7 @@ public abstract class AndroidCameraRecord { private PreviewCallback storedPreviewCallback; private boolean previewStarted; protected int orientationCode; - private static final String tag="Linphone"; + protected static final String tag="Linphone"; private List supportedVideoSizes; public AndroidCameraRecord(RecorderParams parameters) { @@ -47,7 +48,9 @@ public abstract class AndroidCameraRecord { setRotation(parameters.rotation); } - + protected List getSupportedPreviewSizes(Camera.Parameters parameters) { + return Collections.emptyList(); + } public void startPreview() { // FIXME throws exception? if (previewStarted) { @@ -73,7 +76,7 @@ public abstract class AndroidCameraRecord { Camera.Parameters parameters=camera.getParameters(); if (supportedVideoSizes == null) { - supportedVideoSizes = camera.getParameters().getSupportedPreviewSizes(); + supportedVideoSizes = getSupportedPreviewSizes(camera.getParameters()); } parameters.set("camera-id", params.cameraId); @@ -83,18 +86,7 @@ public abstract class AndroidCameraRecord { parameters.setPreviewSize(params.height, params.width); } parameters.setPreviewFrameRate(Math.round(params.fps)); - if (parameters.getSupportedFocusModes().contains(Camera.Parameters.FOCUS_MODE_AUTO)) { - Log.w(tag, "Auto Focus supported by camera device"); - parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO); - } else { - Log.w(tag, "Auto Focus not supported by camera device"); - if (parameters.getSupportedFocusModes().contains(Camera.Parameters.FOCUS_MODE_INFINITY)) { - Log.w(tag, "Infinity Focus supported by camera device"); - parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_INFINITY); - } else { - Log.w(tag, "Infinity Focus not supported by camera device"); - } - } + onSettingCameraParameters(parameters); camera.setParameters(parameters); diff --git a/AndroidCameraRecordBufferedImpl.java b/AndroidCameraRecordBufferedImpl.java index f58698df2..9f8b2d1c0 100644 --- a/AndroidCameraRecordBufferedImpl.java +++ b/AndroidCameraRecordBufferedImpl.java @@ -30,7 +30,7 @@ import android.util.Log; * @author Guillaume Beraudo * */ -public class AndroidCameraRecordBufferedImpl extends AndroidCameraRecordImpl { +public class AndroidCameraRecordBufferedImpl extends AndroidCameraRecordImplAPI5 { public AndroidCameraRecordBufferedImpl(RecorderParams parameters) { diff --git a/AndroidCameraRecordImplAPI5.java b/AndroidCameraRecordImplAPI5.java new file mode 100644 index 000000000..7ff307d03 --- /dev/null +++ b/AndroidCameraRecordImplAPI5.java @@ -0,0 +1,64 @@ +/* +AndroidCameraRecordImplAPI5.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.core; + +import java.util.List; + +import android.hardware.Camera; +import android.hardware.Camera.Parameters; +import android.hardware.Camera.Size; +import android.util.Log; + + +public class AndroidCameraRecordImplAPI5 extends AndroidCameraRecordImpl { + + public AndroidCameraRecordImplAPI5(RecorderParams parameters) { + super(parameters); + } + + @Override + protected void onSettingCameraParameters(Parameters parameters) { + super.onSettingCameraParameters(parameters); + + if (parameters.getSupportedFocusModes().contains(Camera.Parameters.FOCUS_MODE_AUTO)) { + Log.w(tag, "Auto Focus supported by camera device"); + parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO); + } else { + Log.w(tag, "Auto Focus not supported by camera device"); + if (parameters.getSupportedFocusModes().contains(Camera.Parameters.FOCUS_MODE_INFINITY)) { + Log.w(tag, "Infinity Focus supported by camera device"); + parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_INFINITY); + } else { + Log.w(tag, "Infinity Focus not supported by camera device"); + } + } + } + + public static List oneShotSupportedVideoSizes() { + Camera camera = Camera.open(); + List supportedVideoSizes =camera.getParameters().getSupportedPreviewSizes(); + camera.release(); + return supportedVideoSizes; + } + + @Override + protected List getSupportedPreviewSizes(Parameters parameters) { + return parameters.getSupportedPreviewSizes(); + } +} diff --git a/AndroidCameraRecordManager.java b/AndroidCameraRecordManager.java index 4c767b887..c6bae18da 100644 --- a/AndroidCameraRecordManager.java +++ b/AndroidCameraRecordManager.java @@ -24,7 +24,6 @@ import java.util.Map; import org.linphone.core.AndroidCameraRecord.RecorderParams; -import android.hardware.Camera; import android.hardware.Camera.Size; import android.os.Build; import android.util.Log; @@ -41,7 +40,6 @@ import android.view.SurfaceHolder.Callback; * */ public class AndroidCameraRecordManager { - public static final int CAMERA_ID_FIXME_USE_PREFERENCE = 0; private static final int version = Integer.parseInt(Build.VERSION.SDK); private static Map instances = new HashMap(); @@ -52,7 +50,8 @@ public class AndroidCameraRecordManager { } /** - * @param cameraId : see max_camera_id + * Instance for a given camera + * @param cameraId : starting from 0 * @return */ public static final synchronized AndroidCameraRecordManager getInstance(int cameraId) { @@ -69,6 +68,9 @@ public class AndroidCameraRecordManager { return m; } + /** + * @return instance for the default camera + */ public static final synchronized AndroidCameraRecordManager getInstance() { return getInstance(0); } @@ -148,6 +150,8 @@ public class AndroidCameraRecordManager { parameters.surfaceView = surfaceView; if (version >= 8) { recorder = new AndroidCameraRecordBufferedImpl(parameters); + } else if (version >= 5) { + recorder = new AndroidCameraRecordImplAPI5(parameters); } else { recorder = new AndroidCameraRecordImpl(parameters); } @@ -164,6 +168,10 @@ public class AndroidCameraRecordManager { // FIXME select right camera + /** + * Eventually null if API < 5. + * + */ public List supportedVideoSizes() { if (supportedVideoSizes != null) { return supportedVideoSizes; @@ -174,9 +182,12 @@ public class AndroidCameraRecordManager { if (supportedVideoSizes != null) return supportedVideoSizes; } - Camera camera = Camera.open(); - supportedVideoSizes = camera.getParameters().getSupportedPreviewSizes(); - camera.release(); + if (version >= 5) { + supportedVideoSizes = AndroidCameraRecordImplAPI5.oneShotSupportedVideoSizes(); + } + + // eventually null + return supportedVideoSizes; } From 6df330a6b4f6d45a651fd3d27a25d686f752a186 Mon Sep 17 00:00:00 2001 From: Guillaume Beraudo Date: Fri, 3 Dec 2010 16:13:01 +0100 Subject: [PATCH 29/38] Add video codecs preference. Integrate JNI code for getting list of available video codecs. --- LinphoneCoreImpl.java | 16 +++++++++++++++- PayloadTypeImpl.java | 13 ++++++++++++- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/LinphoneCoreImpl.java b/LinphoneCoreImpl.java index 745c8c358..62741c7ef 100644 --- a/LinphoneCoreImpl.java +++ b/LinphoneCoreImpl.java @@ -85,9 +85,10 @@ class LinphoneCoreImpl implements LinphoneCore { 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 native void setRing(long nativePtr, String path); private native String getRing(long nativePtr); + private native long[] listVideoPayloadTypes(long nativePtr); + private static String TAG = "LinphoneCore"; @@ -415,4 +416,17 @@ class LinphoneCoreImpl implements LinphoneCore { // TODO Auto-generated method stub return false; } + + public PayloadType[] listVideoCodecs() { + long[] typesPtr = listVideoPayloadTypes(nativePtr); + if (typesPtr == null) return null; + + PayloadType[] codecs = new PayloadType[typesPtr.length]; + + for (int i=0; i < codecs.length; i++) { + codecs[i] = new PayloadTypeImpl(typesPtr[i]); + } + + return codecs; + } } diff --git a/PayloadTypeImpl.java b/PayloadTypeImpl.java index 28c206786..864b094ff 100644 --- a/PayloadTypeImpl.java +++ b/PayloadTypeImpl.java @@ -24,10 +24,21 @@ class PayloadTypeImpl implements PayloadType { protected final long nativePtr; private native String toString(long ptr); - + private native String getMime(long ptr); + private native int getRate(long ptr); + protected PayloadTypeImpl(long aNativePtr) { nativePtr = aNativePtr; } + + public int getRate() { + return getRate(nativePtr); + } + + public String getMime() { + return getMime(nativePtr); + } + public String toString() { return toString(nativePtr); } From 77745099ba904b7ff0bf1563ab9129c611f50182 Mon Sep 17 00:00:00 2001 From: Guillaume Beraudo Date: Mon, 6 Dec 2010 14:31:49 +0100 Subject: [PATCH 30/38] Fixed current CallParams not copied. Fixed not always sending static image on camera muted. Improved organization of preference activity. --- LinphoneCallImpl.java | 9 +++------ LinphoneCallParamsImpl.java | 11 ++++++----- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/LinphoneCallImpl.java b/LinphoneCallImpl.java index a73492274..74c701a72 100644 --- a/LinphoneCallImpl.java +++ b/LinphoneCallImpl.java @@ -29,7 +29,7 @@ class LinphoneCallImpl implements LinphoneCall { private native boolean isIncoming(long nativePtr); native private long getRemoteAddress(long nativePtr); native private int getState(long nativePtr); - private native long getCurrentParams(long nativePtr); + private native long getCurrentParamsCopy(long nativePtr); private native void enableCamera(long nativePtr, boolean enabled); protected LinphoneCallImpl(long aNativePtr) { @@ -61,11 +61,8 @@ class LinphoneCallImpl implements LinphoneCall { public State getState() { return LinphoneCall.State.fromInt(getState(nativePtr)); } - public LinphoneCallParams getCurrentParamsReadOnly() { - return new LinphoneCallParamsImpl(getCurrentParams(nativePtr)); - } - public LinphoneCallParams getCurrentParamsReadWrite() { - return getCurrentParamsReadOnly().copy(); + public LinphoneCallParams getCurrentParamsCopy() { + return new LinphoneCallParamsImpl(getCurrentParamsCopy(nativePtr)); } public void enableCamera(boolean enabled) { diff --git a/LinphoneCallParamsImpl.java b/LinphoneCallParamsImpl.java index 1242ee996..33d460697 100644 --- a/LinphoneCallParamsImpl.java +++ b/LinphoneCallParamsImpl.java @@ -27,7 +27,7 @@ public class LinphoneCallParamsImpl implements LinphoneCallParams { private native void enableVideo(long nativePtr, boolean b); private native boolean getVideoEnabled(long nativePtr); - private native long copy(long nativePtr); + private native void destroy(long nativePtr); public boolean getVideoEnabled() { @@ -37,9 +37,10 @@ public class LinphoneCallParamsImpl implements LinphoneCallParams { public void setVideoEnabled(boolean b) { enableVideo(nativePtr, b); } - - public LinphoneCallParams copy() { - return new LinphoneCallParamsImpl(copy(nativePtr)); + + @Override + protected void finalize() throws Throwable { + destroy(nativePtr); + super.finalize(); } - } From 2b829b72b6b990b9e6eab653407ac30c7347f4ed Mon Sep 17 00:00:00 2001 From: Guillaume Beraudo Date: Mon, 6 Dec 2010 15:06:59 +0100 Subject: [PATCH 31/38] Fixed unimplemented methods. Full brightness on videocall activity. --- LinphoneProxyConfigImpl.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/LinphoneProxyConfigImpl.java b/LinphoneProxyConfigImpl.java index f1dcb70a4..5bf4971d0 100644 --- a/LinphoneProxyConfigImpl.java +++ b/LinphoneProxyConfigImpl.java @@ -121,4 +121,10 @@ class LinphoneProxyConfigImpl implements LinphoneProxyConfig { throw new LinphoneCoreException("cannot set route ["+routeUri+"]"); } } + public void enablePublish(boolean enable) { + throw new RuntimeException("not implemented"); + } + public boolean publishEnabled() { + throw new RuntimeException("not implemented"); + } } From 16749d14d3f6535b6ba399a5d0d8962b740979ea Mon Sep 17 00:00:00 2001 From: Guillaume Beraudo Date: Tue, 7 Dec 2010 10:32:52 +0100 Subject: [PATCH 32/38] Simplify registration tutorial. --- tutorials/TutorialRegistrationActivity.java | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/tutorials/TutorialRegistrationActivity.java b/tutorials/TutorialRegistrationActivity.java index d87446140..6da10f3d6 100644 --- a/tutorials/TutorialRegistrationActivity.java +++ b/tutorials/TutorialRegistrationActivity.java @@ -42,6 +42,7 @@ public class TutorialRegistrationActivity extends TutorialHelloWorldActivity { private TutorialRegistration tutorial; private Button buttonCall; private Handler mHandler = new Handler(); + private TextView outputText; @Override @@ -55,7 +56,7 @@ public class TutorialRegistrationActivity extends TutorialHelloWorldActivity { sipPasswordWidget.setText(defaultSipPassword); // Output text to the outputText widget - final TextView outputText = (TextView) findViewById(R.id.OutputText); + outputText = (TextView) findViewById(R.id.OutputText); final TutorialNotifier notifier = new AndroidTutorialNotifier(mHandler, outputText); @@ -66,6 +67,7 @@ public class TutorialRegistrationActivity extends TutorialHelloWorldActivity { // Assign call action to call button buttonCall = (Button) findViewById(R.id.CallButton); + buttonCall.setText("Register"); buttonCall.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { TutorialLaunchingThread thread = new TutorialLaunchingThread(); @@ -74,13 +76,9 @@ public class TutorialRegistrationActivity extends TutorialHelloWorldActivity { } }); - // Assign stop action to stop button + // Hide stop button Button buttonStop = (Button) findViewById(R.id.ButtonStop); - buttonStop.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - tutorial.stopMainLoop(); - } - }); + buttonStop.setVisibility(View.GONE); } @@ -92,13 +90,9 @@ public class TutorialRegistrationActivity extends TutorialHelloWorldActivity { tutorial.launchTutorial( sipAddressWidget.getText().toString(), sipPasswordWidget.getText().toString()); - mHandler.post(new Runnable() { - public void run() { - buttonCall.setEnabled(true); - } - }); } catch (LinphoneCoreException e) { e.printStackTrace(); + outputText.setText(e.getMessage() +"\n"+outputText.getText()); } } } From 5566daf9b1dc7451909dcdf3e1c2b730e6ddf896 Mon Sep 17 00:00:00 2001 From: Guillaume Beraudo Date: Thu, 9 Dec 2010 09:08:12 +0100 Subject: [PATCH 33/38] Fixed and improved video test. --- tutorials/JavaCameraRecordImpl.java | 9 ++ tutorials/TestVideoActivity.java | 160 ++++++++++++++++++++++++---- 2 files changed, 150 insertions(+), 19 deletions(-) diff --git a/tutorials/JavaCameraRecordImpl.java b/tutorials/JavaCameraRecordImpl.java index 12594ccef..33cc6b22e 100644 --- a/tutorials/JavaCameraRecordImpl.java +++ b/tutorials/JavaCameraRecordImpl.java @@ -22,6 +22,7 @@ import org.linphone.core.AndroidCameraRecord; import android.hardware.Camera; import android.hardware.Camera.PreviewCallback; +import android.hardware.Camera.Size; import android.util.Log; import android.widget.TextView; @@ -49,6 +50,14 @@ public class JavaCameraRecordImpl extends AndroidCameraRecord implements Preview public void onPreviewFrame(byte[] data, Camera camera) { + Size s = camera.getParameters().getPreviewSize(); + int expectedBuffLength = s.width * s.height * 3 /2; + if (expectedBuffLength != data.length) { + Log.e("Linphone", "onPreviewFrame called with bad buffer length " + data.length + + " whereas expected is " + expectedBuffLength + " don't calling putImage"); + return; + } + if ((count % 2 * fps) == 0) { endTime = System.currentTimeMillis(); averageCalledRate = (100000 * 2 * fps) / (endTime - startTime); diff --git a/tutorials/TestVideoActivity.java b/tutorials/TestVideoActivity.java index 4b4ccc09e..971b8f73f 100644 --- a/tutorials/TestVideoActivity.java +++ b/tutorials/TestVideoActivity.java @@ -15,16 +15,27 @@ 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.core.tutorials; +import java.util.Stack; + import org.linphone.R; import org.linphone.core.AndroidCameraRecord; +import org.linphone.core.VideoSize; import android.app.Activity; import android.os.Bundle; +import android.util.Log; +import android.view.SurfaceHolder; import android.view.SurfaceView; +import android.view.View; +import android.view.SurfaceHolder.Callback; +import android.view.View.OnClickListener; +import android.view.ViewGroup.LayoutParams; +import android.widget.Button; import android.widget.TextView; +import static org.linphone.core.VideoSize.*; /** * Activity for displaying and starting the HelloWorld example on Android phone. @@ -32,38 +43,149 @@ import android.widget.TextView; * @author Guillaume Beraudo * */ -public class TestVideoActivity extends Activity { +public class TestVideoActivity extends Activity implements Callback, OnClickListener { private SurfaceView surfaceView; private static final int rate = 7; + private JavaCameraRecordImpl recorder; + private static String tag = "Linphone"; + private TextView debugView; + private Button nextSize; + private Button changeCamera; + private Button changeOrientation; + private AndroidCameraRecord.RecorderParams params; + + private Stack videoSizes = createSizesToTest(); + private int currentCameraId = 2; + private boolean currentOrientationIsPortrait = false; + private int width; + private int height; + private boolean started; + + - - - @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.videotest); surfaceView=(SurfaceView)findViewById(R.id.videotest_surfaceView); + + nextSize = (Button) findViewById(R.id.test_video_size); + nextSize.setOnClickListener(this); + + changeCamera = (Button) findViewById(R.id.test_video_camera); + changeCamera.setText("Cam"+otherCameraId(currentCameraId)); + changeCamera.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + changeCamera.setText("Cam"+currentCameraId); + currentCameraId = otherCameraId(currentCameraId); + updateRecording(); + } + }); -// SurfaceHolder holder=surfaceView.getHolder(); -// holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); - - AndroidCameraRecord.RecorderParams params = new AndroidCameraRecord.RecorderParams(0); - params.surfaceView = surfaceView; - params.width = 352; - params.height = 288; - params.fps = rate; + changeOrientation = (Button) findViewById(R.id.test_video_orientation); + changeOrientation.setText(orientationToString(!currentOrientationIsPortrait)); + changeOrientation.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + currentOrientationIsPortrait = !currentOrientationIsPortrait; + changeOrientation.setText(orientationToString(!currentOrientationIsPortrait)); - JavaCameraRecordImpl recorder = new JavaCameraRecordImpl(params); - recorder.setDebug((TextView) findViewById(R.id.videotest_debug)); - + if (width == 0 || height == 0) return; + int newWidth = currentOrientationIsPortrait? Math.min(height, width) : Math.max(height, width); + int newHeight = currentOrientationIsPortrait? Math.max(height, width) : Math.min(height, width); + changeSurfaceViewLayout(newWidth, newHeight); // will change width and height on surfaceChanged + } + }); + + SurfaceHolder holder = surfaceView.getHolder(); + holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); + holder.addCallback(this); + + + debugView = (TextView) findViewById(R.id.videotest_debug); + } + + protected void updateRecording() { + if (width == 0 || height == 0) return; + if (recorder != null) recorder.stopPreview(); + + params = new AndroidCameraRecord.RecorderParams(0); + params.surfaceView = surfaceView; + params.width = width; + params.height = height; + params.fps = rate; + params.cameraId = currentCameraId; + + recorder = new JavaCameraRecordImpl(params); +// recorder.setDebug(debugView); + debugView.setText(orientationToString(currentOrientationIsPortrait) + + " w="+width + " h="+height+ " cam"+currentCameraId); + recorder.startPreview(); + + } + + private String orientationToString(boolean orientationIsPortrait) { + return orientationIsPortrait? "Por" : "Lan"; + } + private int otherCameraId(int currentId) { + return (currentId == 2) ? 1 : 2; + } + public void onClick(View v) { + nextSize.setText("Next"); + started=true; + if (videoSizes.isEmpty()) { + videoSizes = createSizesToTest(); + } + + VideoSize size = videoSizes.pop(); + changeSurfaceViewLayout(size.getWidth(), size.getHeight()); + + // on surface changed the recorder will be restarted with new values + // and the surface will be resized } - - - + private void changeSurfaceViewLayout(int width, int height) { + LayoutParams params = surfaceView.getLayoutParams(); + params.height = height; + params.width = width; + surfaceView.setLayoutParams(params); + + } + private Stack createSizesToTest() { + Stack stack = new Stack(); + + stack.push(VideoSize.createStandard(QCIF, false)); + stack.push(VideoSize.createStandard(CIF, false)); + stack.push(VideoSize.createStandard(QVGA, false)); + stack.push(VideoSize.createStandard(HVGA, false)); + stack.push(new VideoSize(640,480)); + stack.push(new VideoSize(800,480)); + return stack; + } + + + + + public void surfaceDestroyed(SurfaceHolder holder) { + surfaceView = null; + Log.d(tag , "Video capture surface destroyed"); + if (recorder != null) recorder.stopPreview(); + } + + public void surfaceCreated(SurfaceHolder holder) { + Log.d(tag , "Video capture surface created"); + } + + public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { + if (!started) return; + if (recorder != null) recorder.stopPreview(); + + this.width = width; + this.height = height; + + updateRecording(); + } } From 90a5674b978b020637a89782fc90321027a3e629 Mon Sep 17 00:00:00 2001 From: Guillaume Beraudo Date: Thu, 9 Dec 2010 09:13:12 +0100 Subject: [PATCH 34/38] Preliminary support for front camera. --- AndroidCameraRecord.java | 41 ++++++++++----- AndroidCameraRecordBufferedImpl.java | 2 +- AndroidCameraRecordImpl.java | 15 +++--- AndroidCameraRecordManager.java | 77 +++++++++++++++++----------- LinphoneCallParamsImpl.java | 5 ++ 5 files changed, 88 insertions(+), 52 deletions(-) diff --git a/AndroidCameraRecord.java b/AndroidCameraRecord.java index 7fa4746bb..a34322f7f 100644 --- a/AndroidCameraRecord.java +++ b/AndroidCameraRecord.java @@ -39,13 +39,14 @@ public abstract class AndroidCameraRecord { private PreviewCallback storedPreviewCallback; private boolean previewStarted; - protected int orientationCode; + protected int displayOrientation; protected static final String tag="Linphone"; private List supportedVideoSizes; + private Size currentPreviewSize; public AndroidCameraRecord(RecorderParams parameters) { this.params = parameters; - setRotation(parameters.rotation); + setDisplayOrientation(parameters.rotation); } protected List getSupportedPreviewSizes(Camera.Parameters parameters) { @@ -75,11 +76,14 @@ public abstract class AndroidCameraRecord { Camera.Parameters parameters=camera.getParameters(); + parameters.set("camera-id",params.cameraId); + camera.setParameters(parameters); + parameters = camera.getParameters(); if (supportedVideoSizes == null) { - supportedVideoSizes = getSupportedPreviewSizes(camera.getParameters()); + supportedVideoSizes = new ArrayList(getSupportedPreviewSizes(parameters)); } - parameters.set("camera-id", params.cameraId); + if (!params.videoDimensionsInverted) { parameters.setPreviewSize(params.width, params.height); } else { @@ -91,6 +95,7 @@ public abstract class AndroidCameraRecord { onSettingCameraParameters(parameters); camera.setParameters(parameters); + currentPreviewSize = camera.getParameters().getPreviewSize(); SurfaceHolder holder = params.surfaceView.getHolder(); try { @@ -140,12 +145,13 @@ public abstract class AndroidCameraRecord { } - void stopPreview() { + public void stopPreview() { if (!previewStarted) return; lowLevelSetPreviewCallback(camera, null); camera.stopPreview(); camera.release(); camera=null; + if (currentPreviewSize != null) currentPreviewSize = null; previewStarted = false; } @@ -158,12 +164,16 @@ public abstract class AndroidCameraRecord { protected abstract void lowLevelSetPreviewCallback(Camera camera, PreviewCallback cb); - public void setRotation(int rotation) { - orientationCode = (4 + 1 - rotation) % 4; + public void setDisplayOrientation(int rotation) { + displayOrientation = rotation; } - protected int getOrientationCode() { - return orientationCode; + protected int rotateCapturedFrame() { + if (params.cameraId == 2) { + return 0; + } else { + return (4 + 1 - displayOrientation) % 4; + } } @@ -175,10 +185,10 @@ public abstract class AndroidCameraRecord { public int width; final long filterDataNativePtr; - int cameraId; - int rotation; + public int cameraId; + public int rotation; public SurfaceView surfaceView; - boolean videoDimensionsInverted; + public boolean videoDimensionsInverted; public RecorderParams(long ptr) { filterDataNativePtr = ptr; @@ -195,4 +205,11 @@ public abstract class AndroidCameraRecord { public List getSupportedVideoSizes() { return new ArrayList(supportedVideoSizes); } + + + protected int getExpectedBufferLength() { + if (currentPreviewSize == null) return -1; + + return currentPreviewSize.width * currentPreviewSize.height * 3 /2; + } } diff --git a/AndroidCameraRecordBufferedImpl.java b/AndroidCameraRecordBufferedImpl.java index 9f8b2d1c0..232d15970 100644 --- a/AndroidCameraRecordBufferedImpl.java +++ b/AndroidCameraRecordBufferedImpl.java @@ -66,7 +66,7 @@ public class AndroidCameraRecordBufferedImpl extends AndroidCameraRecordImplAPI5 protected void onSettingCameraParameters(Parameters parameters) { super.onSettingCameraParameters(parameters); // Only on v8 hardware - camera.setDisplayOrientation(90 * orientationCode); + camera.setDisplayOrientation(90 * displayOrientation); } diff --git a/AndroidCameraRecordImpl.java b/AndroidCameraRecordImpl.java index 2d3087854..daf26ea4e 100644 --- a/AndroidCameraRecordImpl.java +++ b/AndroidCameraRecordImpl.java @@ -20,7 +20,6 @@ package org.linphone.core; import android.hardware.Camera; import android.hardware.Camera.PreviewCallback; -import android.hardware.Camera.Size; import android.util.Log; /** @@ -35,19 +34,19 @@ public class AndroidCameraRecordImpl extends AndroidCameraRecord implements Prev private double timeElapsedBetweenFrames = 0; private long lastFrameTime = 0; private final double expectedTimeBetweenFrames; - private boolean videoDimensionsInverted; + private boolean sizesInverted; public AndroidCameraRecordImpl(RecorderParams parameters) { super(parameters); expectedTimeBetweenFrames = 1d / Math.round(parameters.fps); filterCtxPtr = parameters.filterDataNativePtr; - videoDimensionsInverted = parameters.videoDimensionsInverted; + sizesInverted = parameters.videoDimensionsInverted; storePreviewCallBack(this); } - private native void putImage(long filterCtxPtr, byte[] buffer, int orientation, boolean videoDimensionsInverted); + private native void putImage(long filterCtxPtr, byte[] buffer, int rotate, boolean sizesInverted); public void onPreviewFrame(byte[] data, Camera camera) { @@ -60,8 +59,7 @@ public class AndroidCameraRecordImpl extends AndroidCameraRecord implements Prev return; } - Size s = camera.getParameters().getPreviewSize(); - int expectedBuffLength = s.width * s.height * 3 /2; + int expectedBuffLength = getExpectedBufferLength(); if (expectedBuffLength != data.length) { Log.e("Linphone", "onPreviewFrame called with bad buffer length " + data.length + " whereas expected is " + expectedBuffLength + " don't calling putImage"); @@ -71,7 +69,7 @@ public class AndroidCameraRecordImpl extends AndroidCameraRecord implements Prev long curTime = System.currentTimeMillis(); if (lastFrameTime == 0) { lastFrameTime = curTime; - putImage(filterCtxPtr, data, getOrientationCode(), videoDimensionsInverted); + putImage(filterCtxPtr, data, rotateCapturedFrame(), sizesInverted); return; } @@ -84,10 +82,11 @@ public class AndroidCameraRecordImpl extends AndroidCameraRecord implements Prev timeElapsedBetweenFrames = currentTimeElapsed; // Log.d("onPreviewFrame: ", Integer.toString(data.length)); - putImage(filterCtxPtr, data, getOrientationCode(), videoDimensionsInverted); + putImage(filterCtxPtr, data, rotateCapturedFrame(), sizesInverted); } + @Override protected void lowLevelSetPreviewCallback(Camera camera, PreviewCallback cb) { camera.setPreviewCallback(cb); diff --git a/AndroidCameraRecordManager.java b/AndroidCameraRecordManager.java index c6bae18da..a19413eb7 100644 --- a/AndroidCameraRecordManager.java +++ b/AndroidCameraRecordManager.java @@ -18,9 +18,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package org.linphone.core; -import java.util.HashMap; import java.util.List; -import java.util.Map; import org.linphone.core.AndroidCameraRecord.RecorderParams; @@ -41,38 +39,21 @@ import android.view.SurfaceHolder.Callback; */ public class AndroidCameraRecordManager { private static final int version = Integer.parseInt(Build.VERSION.SDK); - private static Map instances = new HashMap(); - + private static final String tag = "Linphone"; + private static AndroidCameraRecordManager instance; // singleton - private AndroidCameraRecordManager(int cameraId) { - this.cameraId = cameraId; - } + private AndroidCameraRecordManager() {} - /** - * Instance for a given camera - * @param cameraId : starting from 0 - * @return - */ - public static final synchronized AndroidCameraRecordManager getInstance(int cameraId) { - if (cameraId < 0) { - Log.e("Linphone", "Asking unmanageable camera " + cameraId); - return null; - } - - AndroidCameraRecordManager m = instances.get(cameraId); - if (m == null) { - m = new AndroidCameraRecordManager(cameraId); - instances.put(cameraId, m); - } - return m; - } /** - * @return instance for the default camera + * @return instance */ public static final synchronized AndroidCameraRecordManager getInstance() { - return getInstance(0); + if (instance == null) { + instance = new AndroidCameraRecordManager(); + } + return instance; } private AndroidCameraRecord.RecorderParams parameters; @@ -81,11 +62,26 @@ public class AndroidCameraRecordManager { private AndroidCameraRecord recorder; - private final Integer cameraId; + private List supportedVideoSizes; private int rotation; - private static final String tag = "Linphone"; + + private boolean useFrontCamera; + public void setUseFrontCamera(boolean value) { + if (useFrontCamera == value) return; + this.useFrontCamera = value; + + if (parameters != null) { + parameters.cameraId = cameraId(); + if (isRecording()) { + stopVideoRecording(); + tryToStartVideoRecording(); + } + } + } + public boolean isUseFrontCamera() {return useFrontCamera;} + public void setParametersFromFilter(long filterDataPtr, int height, int width, float fps) { @@ -94,15 +90,16 @@ public class AndroidCameraRecordManager { p.fps = fps; p.width = width; p.height = height; - p.cameraId = cameraId; + p.cameraId = cameraId(); p.videoDimensionsInverted = width < height; + // width and height will be inverted in Recorder on startPreview parameters = p; tryToStartVideoRecording(); } public final void setSurfaceView(final SurfaceView sv, final int rotation) { - this.rotation = rotation; + this.rotation = useFrontCamera ? 1 : rotation; SurfaceHolder holder = sv.getHolder(); holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); @@ -206,4 +203,22 @@ public class AndroidCameraRecordManager { parameters = null; } + public int[] doYouSupportThisVideoSize(int[] askedSize) { + final int askedW = askedSize[0]; + final int askedH = askedSize[1]; + Log.d(tag, "w"+askedW); + Log.d(tag, "h"+askedH); + if (useFrontCamera && isPortraitSize(askedW, askedH)) { + return new int[] {askedH, askedW}; // only landscape supported + } else { + return askedSize; + } + } + private boolean isPortraitSize(int width, int height) { + return width < height; + } + + private static final int rearCamId() {return 1;} + private static final int frontCamId() {return 2;} + private final int cameraId() {return useFrontCamera? frontCamId() : rearCamId(); } } diff --git a/LinphoneCallParamsImpl.java b/LinphoneCallParamsImpl.java index 33d460697..ef05eb228 100644 --- a/LinphoneCallParamsImpl.java +++ b/LinphoneCallParamsImpl.java @@ -27,6 +27,7 @@ public class LinphoneCallParamsImpl implements LinphoneCallParams { private native void enableVideo(long nativePtr, boolean b); private native boolean getVideoEnabled(long nativePtr); + private native void audioBandwidth(long nativePtr, int bw); private native void destroy(long nativePtr); @@ -43,4 +44,8 @@ public class LinphoneCallParamsImpl implements LinphoneCallParams { destroy(nativePtr); super.finalize(); } + + public void setAudioBandwidth(int value) { + audioBandwidth(nativePtr, value); + } } From 74096f25ba3d272ce4a267fa18e96707d492308d Mon Sep 17 00:00:00 2001 From: Guillaume Beraudo Date: Thu, 9 Dec 2010 16:09:38 +0100 Subject: [PATCH 35/38] Refactor code to use the new InviteManager which in turn uses the Bandwidth and Camera managers. Improved front camera support. --- AndroidCameraRecordManager.java | 34 ++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/AndroidCameraRecordManager.java b/AndroidCameraRecordManager.java index a19413eb7..c5f409394 100644 --- a/AndroidCameraRecordManager.java +++ b/AndroidCameraRecordManager.java @@ -184,7 +184,7 @@ public class AndroidCameraRecordManager { } // eventually null - + return supportedVideoSizes; } @@ -203,21 +203,33 @@ public class AndroidCameraRecordManager { parameters = null; } - public int[] doYouSupportThisVideoSize(int[] askedSize) { - final int askedW = askedSize[0]; - final int askedH = askedSize[1]; - Log.d(tag, "w"+askedW); - Log.d(tag, "h"+askedH); - if (useFrontCamera && isPortraitSize(askedW, askedH)) { - return new int[] {askedH, askedW}; // only landscape supported + /** + * Naive simple version. + * @param askedSize + * @return + */ + public VideoSize doYouSupportThisVideoSize(VideoSize askedSize) { + Log.d(tag, "Asking camera if it supports size "+askedSize); + if (useFrontCamera && askedSize.isPortrait()) { + return askedSize.createInverted(); // only landscape supported } else { return askedSize; } } - private boolean isPortraitSize(int width, int height) { - return width < height; - } + + private VideoSize closestVideoSize(VideoSize vSize, int defaultSizeCode, boolean defaultIsPortrait) { + VideoSize testSize = vSize.isPortrait() ? vSize.createInverted() : vSize; + + for (Size s : AndroidCameraRecordManager.getInstance().supportedVideoSizes()) { + if (s.height == testSize.getHeight() && s.width == testSize.getWidth()) { + return vSize; + } + } + + return VideoSize.createStandard(defaultSizeCode, defaultIsPortrait); + } + private static final int rearCamId() {return 1;} private static final int frontCamId() {return 2;} private final int cameraId() {return useFrontCamera? frontCamId() : rearCamId(); } From 2961330867f630250f5d4af0c5452eff6fc7a2e9 Mon Sep 17 00:00:00 2001 From: Guillaume Beraudo Date: Thu, 9 Dec 2010 22:52:27 +0100 Subject: [PATCH 36/38] Capture preview is now dynamically resized to fit Linphonecore preferredVideoSize. --- AndroidCameraRecordManager.java | 13 +++++++++++-- LinphoneCoreImpl.java | 2 +- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/AndroidCameraRecordManager.java b/AndroidCameraRecordManager.java index c5f409394..023dcffa4 100644 --- a/AndroidCameraRecordManager.java +++ b/AndroidCameraRecordManager.java @@ -32,7 +32,7 @@ import android.view.SurfaceHolder.Callback; /** - * Manage the video capture; one instance per camera. + * Manage the video capture, only on for all cameras. * * @author Guillaume Beraudo * @@ -81,6 +81,10 @@ public class AndroidCameraRecordManager { } } public boolean isUseFrontCamera() {return useFrontCamera;} + public boolean toggleUseFrontCamera() { + setUseFrontCamera(!useFrontCamera); + return useFrontCamera; + } @@ -132,13 +136,18 @@ public class AndroidCameraRecordManager { tryToStartVideoRecording(); } } - public void toggleMute() { + public boolean toggleMute() { setMuted(!muted); + return muted; } public boolean isMuted() { return muted; } + public void tryResumingVideoRecording() { + if (isRecording()) return; + tryToStartVideoRecording(); + } private void tryToStartVideoRecording() { if (muted || surfaceView == null || parameters == null) return; diff --git a/LinphoneCoreImpl.java b/LinphoneCoreImpl.java index 62741c7ef..b9b8855ca 100644 --- a/LinphoneCoreImpl.java +++ b/LinphoneCoreImpl.java @@ -90,7 +90,7 @@ class LinphoneCoreImpl implements LinphoneCore { private native long[] listVideoPayloadTypes(long nativePtr); - private static String TAG = "LinphoneCore"; + private static final String TAG = "LinphoneCore"; LinphoneCoreImpl(LinphoneCoreListener listener, File userConfig,File factoryConfig,Object userdata) throws IOException { mListener=listener; From bbdc548d74ecea04a3514a3563365abbc4764b53 Mon Sep 17 00:00:00 2001 From: Simon Morlat Date: Fri, 10 Dec 2010 16:42:04 +0100 Subject: [PATCH 37/38] fix orientation in 2.2 --- AndroidCameraRecordBufferedImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AndroidCameraRecordBufferedImpl.java b/AndroidCameraRecordBufferedImpl.java index 232d15970..ad54341c3 100644 --- a/AndroidCameraRecordBufferedImpl.java +++ b/AndroidCameraRecordBufferedImpl.java @@ -66,7 +66,7 @@ public class AndroidCameraRecordBufferedImpl extends AndroidCameraRecordImplAPI5 protected void onSettingCameraParameters(Parameters parameters) { super.onSettingCameraParameters(parameters); // Only on v8 hardware - camera.setDisplayOrientation(90 * displayOrientation); + camera.setDisplayOrientation(90 * rotateCapturedFrame()); } From 1a18fe6207b08e209ada35b1c48617b38e2b828a Mon Sep 17 00:00:00 2001 From: Guillaume Beraudo Date: Mon, 13 Dec 2010 14:04:38 +0100 Subject: [PATCH 38/38] Rotate information according to cameraId. --- AndroidCameraRecord.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/AndroidCameraRecord.java b/AndroidCameraRecord.java index a34322f7f..84ad452ef 100644 --- a/AndroidCameraRecord.java +++ b/AndroidCameraRecord.java @@ -169,7 +169,9 @@ public abstract class AndroidCameraRecord { } protected int rotateCapturedFrame() { - if (params.cameraId == 2) { + if (params.videoDimensionsInverted) { + return 1; // always rotate 90° + } else if (params.cameraId == 2) { return 0; } else { return (4 + 1 - displayOrientation) % 4;