com.gsma.rcs.ri.sharing.video.OutgoingVideoSharing.java Source code

Java tutorial

Introduction

Here is the source code for com.gsma.rcs.ri.sharing.video.OutgoingVideoSharing.java

Source

/*******************************************************************************
 * Software Name : RCS IMS Stack
 * <p/>
 * Copyright (C) 2010-2016 Orange.
 * <p/>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p/>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p/>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/

package com.gsma.rcs.ri.sharing.video;

import com.gsma.rcs.api.connection.ConnectionManager.RcsServiceName;
import com.gsma.rcs.api.connection.utils.ExceptionUtil;
import com.gsma.rcs.api.connection.utils.RcsActivity;
import com.gsma.rcs.ri.R;
import com.gsma.rcs.ri.RiApplication;
import com.gsma.rcs.ri.sharing.video.media.OriginatingVideoPlayer;
import com.gsma.rcs.ri.sharing.video.media.VideoPlayerListener;
import com.gsma.rcs.ri.sharing.video.media.VideoSurfaceView;
import com.gsma.rcs.ri.utils.ContactListAdapter;
import com.gsma.rcs.ri.utils.ContactUtil;
import com.gsma.rcs.ri.utils.LogUtils;
import com.gsma.rcs.ri.utils.RcsContactUtil;
import com.gsma.rcs.ri.utils.RcsSessionUtil;
import com.gsma.services.rcs.RcsGenericException;
import com.gsma.services.rcs.RcsPersistentStorageException;
import com.gsma.services.rcs.RcsServiceException;
import com.gsma.services.rcs.contact.ContactId;
import com.gsma.services.rcs.sharing.video.VideoDescriptor;
import com.gsma.services.rcs.sharing.video.VideoSharing;
import com.gsma.services.rcs.sharing.video.VideoSharingListener;
import com.gsma.services.rcs.sharing.video.VideoSharingService;

import com.orangelabs.rcs.core.ims.protocol.rtp.codec.video.h264.H264Config;
import com.orangelabs.rcs.core.ims.protocol.rtp.format.video.CameraOptions;
import com.orangelabs.rcs.core.ims.protocol.rtp.format.video.Orientation;

import android.Manifest;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.PixelFormat;
import android.hardware.Camera;
import android.hardware.Camera.Parameters;
import android.hardware.Camera.Size;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.app.ActivityCompat;
import android.util.Log;
import android.view.Display;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;

import java.lang.reflect.Method;
import java.util.List;
import java.util.Set;

/**
 * Initiate video sharing.
 *
 * @author Jean-Marc AUFFRET
 * @author Philippe LEMORDANT
 */
public class OutgoingVideoSharing extends RcsActivity implements VideoPlayerListener, SurfaceHolder.Callback {

    /**
     * UI handler
     */
    private final Handler handler = new Handler();

    /**
     * Video sharing
     */
    private VideoSharing mVideoSharing;

    /**
     * Video sharing Id
     */
    private String mSharingId;

    /**
     * Video player<br>
     * Note: this field is intentionally static
     */
    private static OriginatingVideoPlayer mVideoPlayer;

    /**
     * Camera of the device
     */
    private Camera mCamera;

    /**
     * Opened camera id
     */
    private CameraOptions mOpenedCameraId = CameraOptions.FRONT;

    /**
     * Camera preview started flag
     */
    private boolean mCameraPreviewRunning = false;

    /**
     * Video width
     */
    private int mVideoWidth = H264Config.QCIF_WIDTH;

    /**
     * Video height
     */
    private int mVideoHeight = H264Config.QCIF_HEIGHT;

    /**
     * Number of cameras
     */
    private int mNbfCameras = 1;

    /**
     * Live video preview
     */
    private VideoSurfaceView mVideoView;

    /**
     * Video surface holder
     */
    private SurfaceHolder mSurface;

    /**
     * Spinner for contact selection
     */
    private Spinner mSpinner;

    private Button mInviteBtn;

    private Button mDialBtn;

    private Button mSwitchCamBtn;

    private ContactId mContact;

    /**
     * Session is started and video format is then negotiated.
     */
    private boolean mStarted = false;

    /**
     * Preview surface view is created
     */
    private boolean mIsSurfaceCreated;

    private static final String LOGTAG = LogUtils.getTag(OutgoingVideoSharing.class.getSimpleName());

    private static final String SAVE_SHARING_ID = "sharingId";

    private static final String SAVE_VIDEO_HEIGHT = "videoHeight";

    private static final String SAVE_VIDEO_WIDTH = "videoWidth";

    private static final String SAVE_NB_OF_CAMERAS = "numberOfCameras";

    private static final String SAVE_OPENED_CAMERA_ID = "openedCameraId";

    /**
     * We save the remote contact into the activity bundle.<br>
     * This information could also be retrieved from session instance.
     */
    private static final String SAVE_REMOTE_CONTACT = "remoteContact";

    /**
     * We save this information into the activity bundle.<br>
     * This information could also be retrieved from session instance state.
     */
    private static final String SAVE_STARTED = "started";

    /*
     * (non-Javadoc)
     * @see android.app.Activity#onCreate(android.os.Bundle)
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Always on window
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);

        // Set layout
        setContentView(R.layout.video_sharing_outgoing);

        // Set the contact selector
        mSpinner = (Spinner) findViewById(R.id.contact);
        ContactListAdapter adapter = ContactListAdapter.createRcsContactListAdapter(this);
        mSpinner.setAdapter(adapter);

        // Saved datas
        if (savedInstanceState == null) {
            mNbfCameras = getNumberOfCameras();
        } else {
            mSharingId = savedInstanceState.getString(SAVE_SHARING_ID);
            mNbfCameras = savedInstanceState.getInt(SAVE_NB_OF_CAMERAS);
            mVideoHeight = savedInstanceState.getInt(SAVE_VIDEO_HEIGHT);
            mVideoWidth = savedInstanceState.getInt(SAVE_VIDEO_WIDTH);
            mOpenedCameraId = CameraOptions.convert(savedInstanceState.getInt(SAVE_OPENED_CAMERA_ID));
            mContact = savedInstanceState.getParcelable(SAVE_REMOTE_CONTACT);
            mStarted = savedInstanceState.getBoolean(SAVE_STARTED);
        }
        if (LogUtils.isActive) {
            Log.d(LOGTAG, "Sharing ID " + mSharingId + " Nb of cameras=" + mNbfCameras + " active camera="
                    + mOpenedCameraId);
        }
        if (LogUtils.isActive) {
            Log.d(LOGTAG, "Resolution: " + mVideoWidth + "x" + mVideoHeight);
        }

        // Set button callback
        mInviteBtn = (Button) findViewById(R.id.invite_btn);
        mInviteBtn.setOnClickListener(btnInviteListener);
        mDialBtn = (Button) findViewById(R.id.dial_btn);
        mDialBtn.setOnClickListener(btnDialListener);
        mSwitchCamBtn = (Button) findViewById(R.id.switch_cam_btn);

        // Disable button if no contact available
        if (adapter == null || adapter.getCount() == 0) {
            mDialBtn.setEnabled(false);
            mInviteBtn.setEnabled(false);
        }

        // Get camera info
        if (mNbfCameras > 1) {
            boolean backAvailable = checkCameraSize(CameraOptions.BACK);
            boolean frontAvailable = checkCameraSize(CameraOptions.FRONT);
            if (frontAvailable && backAvailable) {
                mSwitchCamBtn.setOnClickListener(btnSwitchCamListener);
            } else if (frontAvailable) {
                mOpenedCameraId = CameraOptions.FRONT;
                mSwitchCamBtn.setVisibility(View.INVISIBLE);
            } else if (backAvailable) {
                mOpenedCameraId = CameraOptions.BACK;
                mSwitchCamBtn.setVisibility(View.INVISIBLE);
            } else {
                if (LogUtils.isActive) {
                    Log.d(LOGTAG, "No camera available for encoding");
                }
            }
        } else {
            if (checkCameraSize(CameraOptions.FRONT)) {
                mSwitchCamBtn.setVisibility(View.INVISIBLE);
            } else {
                if (LogUtils.isActive) {
                    Log.d(LOGTAG, "No camera available for encoding");
                }
            }
        }

        // Create the live video view
        mVideoView = (VideoSurfaceView) findViewById(R.id.video_preview);
        mVideoView.setAspectRatio(mVideoWidth, mVideoHeight);
        if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
            mVideoView.setAspectRatio(mVideoWidth, mVideoHeight);

        } else {
            mVideoView.setAspectRatio(mVideoHeight, mVideoWidth);
        }

        mSurface = mVideoView.getHolder();
        mSurface.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
        mSurface.setKeepScreenOn(true);
        mSurface.addCallback(this);

        // Check if session in progress
        if (mSharingId != null) {
            // Sharing in progress
            mDialBtn.setVisibility(View.GONE);
            mInviteBtn.setVisibility(View.GONE);
            mSpinner.setVisibility(View.GONE);
            mSwitchCamBtn.setEnabled((mNbfCameras > 1));
            handler.post(continueOutgoingSessionRunnable);
            displayRemoteContact();
        } else {
            // Sharing not yet initiated
            mDialBtn.setVisibility(View.VISIBLE);
            mInviteBtn.setVisibility(View.VISIBLE);
            mSwitchCamBtn.setEnabled(false);

            boolean canInitiate = true;
            if (adapter == null || adapter.getCount() == 0) {
                canInitiate = false;
            }
            mDialBtn.setEnabled(canInitiate);
            mInviteBtn.setEnabled(canInitiate);
        }

        // Register to API connection manager
        if (!isServiceConnected(RcsServiceName.VIDEO_SHARING)) {
            showMessageThenExit(R.string.label_service_not_available);
            return;
        }
        startMonitorServices(RcsServiceName.VIDEO_SHARING);

        // Add service listener
        try {
            VideoSharingService vshService = getVideoSharingApi();
            if (mSharingId != null) {
                // Sharing is in progress: get sharing session
                mVideoSharing = vshService.getVideoSharing(mSharingId);
                if (mStarted) {
                    displayVideoFormat();
                }
            }
            vshService.addEventListener(vshListener);
            if (LogUtils.isActive) {
                Log.d(LOGTAG, "onCreate video sharing");
            }
        } catch (RcsServiceException e) {
            showExceptionThenExit(e);
        }
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putString(SAVE_SHARING_ID, mSharingId);
        outState.putInt(SAVE_VIDEO_HEIGHT, mVideoHeight);
        outState.putInt(SAVE_VIDEO_WIDTH, mVideoWidth);
        outState.putInt(SAVE_NB_OF_CAMERAS, mNbfCameras);
        outState.putInt(SAVE_OPENED_CAMERA_ID, mOpenedCameraId.getValue());
        outState.putParcelable(SAVE_REMOTE_CONTACT, mContact);
        outState.putBoolean(SAVE_STARTED, mStarted);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        if (!isServiceConnected(RcsServiceName.VIDEO_SHARING)) {
            return;
        }
        try {
            getVideoSharingApi().removeEventListener(vshListener);

        } catch (RcsServiceException e) {
            Log.w(LOGTAG, ExceptionUtil.getFullStackTrace(e));
        }
        closeCamera();
    }

    /**
     * Dial button listener
     */
    // TODO initialize
    private OnClickListener btnDialListener = new OnClickListener() {
        public void onClick(View v) {

            // get selected phone number
            ContactListAdapter adapter = (ContactListAdapter) mSpinner.getAdapter();
            String phoneNumber = adapter.getSelectedNumber(mSpinner.getSelectedView());

            // Initiate a GSM call before to be able to share content
            Intent intent = new Intent(Intent.ACTION_CALL);
            intent.setData(Uri.parse("tel:".concat(phoneNumber)));
            if (ActivityCompat.checkSelfPermission(OutgoingVideoSharing.this,
                    Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
                return;
            }
            startActivity(intent);
        }
    };

    /**
     * Invite button listener
     */
    private OnClickListener btnInviteListener = new OnClickListener() {
        public void onClick(View v) {
            // Check if the service is available
            try {
                if (!getVideoSharingApi().isServiceRegistered()) {
                    showMessage(R.string.label_service_not_available);
                    return;
                }
            } catch (RcsServiceException e) {
                showExceptionThenExit(e);
            }

            // Get the remote contact
            ContactListAdapter adapter = (ContactListAdapter) mSpinner.getAdapter();
            String phoneNumber = adapter.getSelectedNumber(mSpinner.getSelectedView());

            mContact = ContactUtil.formatContact(phoneNumber);

            new Thread() {
                public void run() {
                    try {
                        // Create the video player
                        mVideoPlayer = new OriginatingVideoPlayer(OutgoingVideoSharing.this);

                        // Open the camera
                        openCamera();

                        // Initiate sharing
                        mVideoSharing = getVideoSharingApi().shareVideo(mContact, mVideoPlayer);
                        mSharingId = mVideoSharing.getSharingId();

                    } catch (final RcsServiceException e) {
                        // Free the camera
                        closeCamera();

                        handler.post(new Runnable() {
                            public void run() {
                                showExceptionThenExit(e);
                            }
                        });
                    }
                }
            }.start();

            mSwitchCamBtn.setEnabled(true);

            // Hide buttons
            mInviteBtn.setVisibility(View.GONE);
            mDialBtn.setVisibility(View.GONE);
            mSpinner.setVisibility(View.GONE);
            displayRemoteContact();
        }
    };

    /**
     * Switch camera button listener
     */
    private View.OnClickListener btnSwitchCamListener = new View.OnClickListener() {
        public void onClick(View v) {
            // Switch camera
            switchCamera();
        }
    };

    private void quitSession() {
        try {
            if (mVideoSharing != null && VideoSharing.State.STARTED == mVideoSharing.getState()) {
                if (LogUtils.isActive) {
                    Log.d(LOGTAG, "Abort sharing");
                }
                mVideoSharing.abortSharing();
            }
        } catch (RcsServiceException e) {
            showException(e);

        } finally {
            mVideoSharing = null;
            // Exit activity
            finish();
        }
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (KeyEvent.KEYCODE_BACK == keyCode) {
            if (LogUtils.isActive) {
                Log.d(LOGTAG, "Back key pressed");
            }
            try {
                if (mVideoSharing == null || !RcsSessionUtil.isAllowedToAbortVideoSharingSession(mVideoSharing)) {
                    finish();
                    return true;
                }
                AlertDialog.Builder builder = new AlertDialog.Builder(this);
                builder.setTitle(R.string.label_confirm_close);
                builder.setPositiveButton(R.string.label_ok, new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int which) {
                        quitSession();
                    }
                });
                builder.setNegativeButton(R.string.label_cancel, new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int which) {
                    }
                });
                builder.setCancelable(true);
                registerDialog(builder.show());
                return true;

            } catch (RcsServiceException e) {
                showException(e);
            }
        }
        return super.onKeyDown(keyCode, event);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = new MenuInflater(getApplicationContext());
        inflater.inflate(R.menu.menu_video_sharing, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case R.id.menu_close_session:
            quitSession();
            break;
        }
        return true;
    }

    /*-------------------------- Camera methods ------------------*/

    /**
     * Open the camera
     */
    private synchronized void openCamera() {
        if (mCamera != null) {
            if (LogUtils.isActive) {
                Log.d(LOGTAG, "Already opened camera");
            }
            return;

        }
        openCamera(mOpenedCameraId);
        if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
            mVideoView.setAspectRatio(mVideoWidth, mVideoHeight);
        } else {
            mVideoView.setAspectRatio(mVideoHeight, mVideoWidth);
        }
        // Start camera
        mCamera.setPreviewCallback(mVideoPlayer);
        startCameraPreview();
    }

    /**
     * Close the camera
     */
    private synchronized void closeCamera() {
        if (mCamera == null) {
            return;

        }
        if (LogUtils.isActive) {
            Log.d(LOGTAG, "Close camera");
        }
        mCamera.setPreviewCallback(null);
        if (mCameraPreviewRunning) {
            mCameraPreviewRunning = false;
            mCamera.stopPreview();
        }
        mCamera.release();
        mCamera = null;
    }

    /**
     * Switch the camera
     */
    private synchronized void switchCamera() {
        if (LogUtils.isActive) {
            Log.d(LOGTAG, "Switch camera");
        }
        closeCamera();
        // Open the other camera
        if (mOpenedCameraId.getValue() == CameraOptions.BACK.getValue()) {
            mOpenedCameraId = CameraOptions.FRONT;
        } else {
            mOpenedCameraId = CameraOptions.BACK;
        }

        openCamera();
    }

    /**
     * Check if good camera sizes are available for encoder. Must be used only before open camera.
     *
     * @param cameraId the camera ID
     * @return false if the camera don't have the good preview size for the encoder
     */
    boolean checkCameraSize(CameraOptions cameraId) {
        boolean sizeAvailable = false;
        Camera camera = null;
        Method method = getCameraOpenMethod();
        if (method != null) {
            try {
                camera = (Camera) method.invoke(camera, new Object[] { cameraId.getValue() });
            } catch (Exception e) {
                camera = Camera.open();
            }
        } else {
            camera = Camera.open();
        }
        if (camera == null) {
            return false;

        }
        // Check common sizes
        Parameters param = camera.getParameters();
        List<Camera.Size> sizes = param.getSupportedPreviewSizes();
        for (Camera.Size size : sizes) {
            if ((size.width == H264Config.QVGA_WIDTH && size.height == H264Config.QVGA_HEIGHT)
                    || (size.width == H264Config.CIF_WIDTH && size.height == H264Config.CIF_HEIGHT)
                    || (size.width == H264Config.VGA_WIDTH && size.height == H264Config.VGA_HEIGHT)) {
                sizeAvailable = true;
                break;
            }
        }

        // Release camera
        camera.release();

        return sizeAvailable;
    }

    /**
     * Start the camera preview
     */
    private void startCameraPreview() {
        if (mCamera == null) {
            return;

        }
        // Camera settings
        Camera.Parameters p = mCamera.getParameters();
        p.setPreviewFormat(PixelFormat.YCbCr_420_SP);

        // Orientation
        Display display = ((WindowManager) getSystemService(WINDOW_SERVICE)).getDefaultDisplay();
        switch (display.getRotation()) {
        case Surface.ROTATION_0:
            if (LogUtils.isActive) {
                Log.d(LOGTAG, "ROTATION_0");
            }
            if (mOpenedCameraId == CameraOptions.FRONT) {
                mVideoPlayer.setOrientation(Orientation.ROTATE_90_CCW);
            } else {
                mVideoPlayer.setOrientation(Orientation.ROTATE_90_CW);
            }
            mCamera.setDisplayOrientation(90);
            break;

        case Surface.ROTATION_90:
            if (LogUtils.isActive) {
                Log.d(LOGTAG, "ROTATION_90");
            }
            mVideoPlayer.setOrientation(Orientation.NONE);
            break;

        case Surface.ROTATION_180:
            if (LogUtils.isActive) {
                Log.d(LOGTAG, "ROTATION_180");
            }
            if (mOpenedCameraId == CameraOptions.FRONT) {
                mVideoPlayer.setOrientation(Orientation.ROTATE_90_CW);
            } else {
                mVideoPlayer.setOrientation(Orientation.ROTATE_90_CCW);
            }
            mCamera.setDisplayOrientation(270);
            break;

        case Surface.ROTATION_270:
            if (LogUtils.isActive) {
                Log.d(LOGTAG, "ROTATION_270");
            }
            if (mOpenedCameraId == CameraOptions.FRONT) {
                mVideoPlayer.setOrientation(Orientation.ROTATE_180);
            } else {
                mVideoPlayer.setOrientation(Orientation.ROTATE_180);
            }
            mCamera.setDisplayOrientation(180);
            break;
        }

        // Check if preview size is supported
        if (isPreviewSizeSupported(p, mVideoWidth, mVideoHeight)) {
            // Use the existing size without resizing
            p.setPreviewSize(mVideoWidth, mVideoHeight);
            // TODO videoPlayer.activateResizing(videoWidth, videoHeight); //
            // same size = no
            // resizing
            if (LogUtils.isActive) {
                Log.d(LOGTAG, "Camera preview initialized with size " + mVideoWidth + "x" + mVideoHeight);
            }
        } else {
            // Check if can use a other known size (QVGA, CIF or VGA)
            int w = 0;
            int h = 0;
            for (Camera.Size size : p.getSupportedPreviewSizes()) {
                w = size.width;
                h = size.height;
                if ((w == H264Config.QVGA_WIDTH && h == H264Config.QVGA_HEIGHT)
                        || (w == H264Config.CIF_WIDTH && h == H264Config.CIF_HEIGHT)
                        || (w == H264Config.VGA_WIDTH && h == H264Config.VGA_HEIGHT)) {
                    break;
                }
            }

            if (w != 0) {
                p.setPreviewSize(w, h);
                // TODO does not work if default sizes are not supported like
                // for Samsung S5 mini
                // mVideoPlayer.activateResizing(w, h);
                if (LogUtils.isActive) {
                    Log.d(LOGTAG, "Camera preview initialized with size " + w + "x" + h + " with a resizing to "
                            + mVideoWidth + "x" + mVideoHeight);
                }
            } else {
                // The camera don't have known size, we can't use it
                if (LogUtils.isActive) {
                    Log.d(LOGTAG,
                            "Camera preview can't be initialized with size " + mVideoWidth + "x" + mVideoHeight);
                }
                Toast.makeText(this, getString(R.string.label_session_failed, "Camera is not compatible"),
                        Toast.LENGTH_SHORT).show();
                quitSession();
                return;

            }
        }

        // Set camera parameters
        mCamera.setParameters(p);
        try {
            mCamera.setPreviewDisplay(mVideoView.getHolder());
            mCamera.startPreview();
            mCameraPreviewRunning = true;
        } catch (Exception e) {
            mCamera = null;
        }
    }

    /**
     * Get Camera "open" Method
     *
     * @return Method
     */
    private Method getCameraOpenMethod() {
        ClassLoader classLoader = OutgoingVideoSharing.class.getClassLoader();
        try {
            Class<?> cameraClass = classLoader.loadClass("android.hardware.Camera");
            try {
                return cameraClass.getMethod("open", new Class[] { int.class });
            } catch (NoSuchMethodException ignored) {
            }
        } catch (ClassNotFoundException ignored) {
        }
        return null;
    }

    /**
     * Open the camera
     *
     * @param cameraId Camera ID
     */
    private void openCamera(CameraOptions cameraId) {
        Method method = getCameraOpenMethod();
        if (mNbfCameras > 1 && method != null) {
            try {
                int hCamId = 0;
                if (cameraId == CameraOptions.FRONT) {
                    Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
                    for (int id = 0; id < mNbfCameras; id++) {
                        Camera.getCameraInfo(id, cameraInfo);
                        if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
                            hCamId = id;
                            break;
                        }
                    }
                }
                mCamera = (Camera) method.invoke(mCamera, new Object[] { hCamId });
                mOpenedCameraId = cameraId;
            } catch (Exception e) {
                mCamera = Camera.open();
                mOpenedCameraId = CameraOptions.BACK;
            }
        } else {
            mCamera = Camera.open();
            mOpenedCameraId = CameraOptions.BACK;
        }
        if (mVideoPlayer != null) {
            mVideoPlayer.setCameraId(mOpenedCameraId.getValue());
        }
        if (LogUtils.isActive) {
            Log.d(LOGTAG, "Open camera ".concat(mOpenedCameraId.toString()));
        }
    }

    /**
     * Get Camera "numberOfCameras" Method
     *
     * @return Method
     */
    private Method getCameraNumberOfCamerasMethod() {
        ClassLoader classLoader = OutgoingVideoSharing.class.getClassLoader();
        try {
            Class<?> cameraClass = classLoader.loadClass("android.hardware.Camera");
            try {
                return cameraClass.getMethod("getNumberOfCameras", (Class[]) null);
            } catch (NoSuchMethodException ignored) {
            }
        } catch (ClassNotFoundException ignored) {
        }
        return null;
    }

    /**
     * Get number of cameras
     *
     * @return number of cameras
     */
    private int getNumberOfCameras() {
        Method method = getCameraNumberOfCamerasMethod();
        if (method != null) {
            try {
                return (Integer) method.invoke(null, (Object[]) null);
            } catch (Exception e) {
                return 1;
            }
        } else {
            return 1;
        }
    }

    /*-------------------------- Session callbacks ------------------*/

    /**
     * Video sharing listener
     */
    private VideoSharingListener vshListener = new VideoSharingListener() {
        @Override
        public void onStateChanged(ContactId contact, String sharingId, final VideoSharing.State state,
                VideoSharing.ReasonCode reasonCode) {
            // Discard event if not for current sharingId
            if (mSharingId == null || !mSharingId.equals(sharingId)) {
                return;
            }
            if (LogUtils.isActive) {
                Log.d(LOGTAG, "onStateChanged contact=" + contact + " sharingId=" + sharingId + " state=" + state
                        + " reason=" + reasonCode);
            }
            final String _reasonCode = RiApplication.sVideoReasonCodes[reasonCode.toInt()];
            handler.post(new Runnable() {
                public void run() {
                    switch (state) {
                    case STARTED:
                        mStarted = true;
                        displayVideoFormat();

                        // Start the player
                        mVideoPlayer.open();
                        mVideoPlayer.start();

                        // Update camera button
                        Button switchCamBtn = (Button) findViewById(R.id.switch_cam_btn);
                        switchCamBtn.setEnabled(true);

                        // Session is established : hide progress dialog
                        break;

                    case ABORTED:
                        // Stop the player
                        mVideoPlayer.stop();
                        mVideoPlayer.close();

                        // Release the camera
                        closeCamera();

                        // Display message info and exit
                        showMessageThenExit(getString(R.string.label_sharing_aborted, _reasonCode));
                        break;

                    case REJECTED:
                        // Release the camera
                        closeCamera();

                        showMessageThenExit(getString(R.string.label_sharing_rejected, _reasonCode));
                        break;

                    case FAILED:
                        // Stop the player
                        mVideoPlayer.stop();
                        mVideoPlayer.close();

                        // Release the camera
                        closeCamera();

                        // Display error info and exit
                        showMessageThenExit(getString(R.string.label_sharing_failed, _reasonCode));
                        break;

                    default:
                        if (LogUtils.isActive) {
                            Log.d(LOGTAG, "onStateChanged ".concat(getString(R.string.label_vsh_state_changed,
                                    RiApplication.sVideoSharingStates[state.toInt()], _reasonCode)));
                        }
                    }
                }
            });
        }

        @Override
        public void onDeleted(ContactId contact, Set<String> sharingIds) {
            if (LogUtils.isActive) {
                Log.w(LOGTAG, "onDeleted contact=" + contact + " sharingIds=" + sharingIds);
            }
        }
    };

    /*-------------------------- Video player callbacks ------------------*/

    /**
     * Callback called when the player is opened
     */
    public void onPlayerOpened() {
        if (LogUtils.isActive) {
            Log.d(LOGTAG, "onPlayerOpened");
        }
    }

    /**
     * Callback called when the player is started
     */
    public void onPlayerStarted() {
        if (LogUtils.isActive) {
            Log.d(LOGTAG, "onPlayerStarted");
        }
    }

    /**
     * Callback called when the player is stopped
     */
    public void onPlayerStopped() {
        if (LogUtils.isActive) {
            Log.d(LOGTAG, "onPlayerStopped");
        }
    }

    /**
     * Callback called when the player is closed
     */
    public void onPlayerClosed() {
        if (LogUtils.isActive) {
            Log.d(LOGTAG, "onPlayerClosed");
        }
    }

    /**
     * Callback called when the player has failed
     */
    public void onPlayerError() {
        // TODO
        if (LogUtils.isActive) {
            Log.d(LOGTAG, "onPlayerError");
        }
    }

    /**
     * Callback called when the player has been resized
     */
    public void onPlayerResized(int width, int height) {
        // TODO
        if (LogUtils.isActive) {
            Log.d(LOGTAG, "onPlayerResized");
        }
    }

    /**
     * Check if preview size is supported
     *
     * @param parameters Camera parameters
     * @param width width
     * @param height height
     * @return True if supported
     */
    private boolean isPreviewSizeSupported(Parameters parameters, int width, int height) {
        List<Camera.Size> sizes = parameters.getSupportedPreviewSizes();
        for (Size size : sizes) {
            if (size.width == width && size.height == height) {
                return true;

            }
        }
        return false;
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        mIsSurfaceCreated = true;
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        mIsSurfaceCreated = true;
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        mIsSurfaceCreated = false;
    }

    /**
     * Runnable to continue outgoing session.<br>
     * Note: the surface view be created to display preview.
     */
    private Runnable continueOutgoingSessionRunnable = new Runnable() {
        private int delay = 0;

        @Override
        public void run() {
            if (mIsSurfaceCreated) {
                // Open camera only once surface is created
                openCamera();
            } else {
                delay += 200;
                handler.removeCallbacks(this);
                if (delay < 2000) {
                    if (LogUtils.isActive) {
                        Log.e(LOGTAG, "Delaying continue Outgoing");
                    }
                    handler.postDelayed(this, delay);
                }
            }
        }
    };

    /**
     * Display video format
     */
    private void displayVideoFormat() {
        try {
            VideoDescriptor videoDescriptor = mVideoSharing.getVideoDescriptor();
            String format = mVideoSharing.getVideoEncoding() + " " + videoDescriptor.getWidth() + "x"
                    + videoDescriptor.getHeight();
            TextView fmtView = (TextView) findViewById(R.id.video_format);
            fmtView.setText(format);

        } catch (RcsPersistentStorageException | RcsGenericException e) {
            Log.w(LOGTAG, ExceptionUtil.getFullStackTrace(e));
        }
    }

    /**
     * Display remote contact
     */
    private void displayRemoteContact() {
        TextView fromTextView = (TextView) findViewById(R.id.remote);
        String displayName = RcsContactUtil.getInstance(this).getDisplayName(mContact);
        fromTextView.setText(displayName);
    }
}