linphone-android/src/android/org/linphone/call/CallVideoFragment.java
2018-01-02 15:40:36 +01:00

384 lines
12 KiB
Java

package org.linphone.call;
/*
CallVideoFragment.java
Copyright (C) 2017 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import org.linphone.LinphoneManager;
import org.linphone.LinphonePreferences;
import org.linphone.LinphoneService;
import org.linphone.LinphoneUtils;
import org.linphone.R;
import org.linphone.compatibility.Compatibility;
import org.linphone.compatibility.CompatibilityScaleGestureDetector;
import org.linphone.compatibility.CompatibilityScaleGestureListener;
import org.linphone.core.Call;
import org.linphone.core.Core;
import org.linphone.core.VideoDefinition;
import org.linphone.mediastream.Log;
import org.linphone.mediastream.video.AndroidVideoWindowImpl;
import android.app.Fragment;
import android.content.Context;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.view.GestureDetector;
import android.view.GestureDetector.OnDoubleTapListener;
import android.view.GestureDetector.OnGestureListener;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.ViewGroup;
import android.widget.RelativeLayout;
public class CallVideoFragment extends Fragment implements OnGestureListener, OnDoubleTapListener, CompatibilityScaleGestureListener {
private SurfaceView mVideoView;
private SurfaceView mCaptureView;
private AndroidVideoWindowImpl androidVideoWindowImpl;
private GestureDetector mGestureDetector;
private float mZoomFactor = 1.f;
private float mZoomCenterX, mZoomCenterY;
private CompatibilityScaleGestureDetector mScaleDetector;
private CallActivity inCallActivity;
private int previewX, previewY;
@SuppressWarnings("deprecation") // Warning useless because value is ignored and automatically set by new APIs.
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view;
if (LinphoneManager.getLc().hasCrappyOpengl()) {
view = inflater.inflate(R.layout.video_no_opengl, container, false);
} else {
view = inflater.inflate(R.layout.video, container, false);
}
mVideoView = (SurfaceView) view.findViewById(R.id.videoSurface);
mCaptureView = (SurfaceView) view.findViewById(R.id.videoCaptureSurface);
mCaptureView.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); // Warning useless because value is ignored and automatically set by new APIs.
fixZOrder(mVideoView, mCaptureView);
androidVideoWindowImpl = new AndroidVideoWindowImpl(mVideoView, mCaptureView, new AndroidVideoWindowImpl.VideoWindowListener() {
public void onVideoRenderingSurfaceReady(AndroidVideoWindowImpl vw, SurfaceView surface) {
mVideoView = surface;
LinphoneManager.getLc().setNativeVideoWindowId(vw);
}
public void onVideoRenderingSurfaceDestroyed(AndroidVideoWindowImpl vw) {
}
public void onVideoPreviewSurfaceReady(AndroidVideoWindowImpl vw, SurfaceView surface) {
mCaptureView = surface;
LinphoneManager.getLc().setNativePreviewWindowId(mCaptureView);
resizePreview();
}
public void onVideoPreviewSurfaceDestroyed(AndroidVideoWindowImpl vw) {
}
});
mVideoView.setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
if (mScaleDetector != null) {
mScaleDetector.onTouchEvent(event);
}
mGestureDetector.onTouchEvent(event);
if (inCallActivity != null) {
inCallActivity.displayVideoCallControlsIfHidden();
}
return true;
}
});
mCaptureView.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
switch (motionEvent.getAction()) {
case MotionEvent.ACTION_DOWN:
previewX = (int) motionEvent.getX();
previewY = (int) motionEvent.getY();
break;
case MotionEvent.ACTION_MOVE:
int x = (int) motionEvent.getX();
int y = (int) motionEvent.getY();
RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams)mCaptureView.getLayoutParams();
lp.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM, 0); // Clears the rule, as there is no removeRule until API 17.
lp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT, 0);
int left = lp.leftMargin + (x - previewX);
int top = lp.topMargin + (y - previewY);
lp.leftMargin = left;
lp.topMargin = top;
view.setLayoutParams(lp);
break;
}
return true;
}
});
return view;
}
@Override
public void onStart() {
super.onStart();
inCallActivity = (CallActivity) getActivity();
if (inCallActivity != null) {
inCallActivity.bindVideoFragment(this);
}
}
private void resizePreview() {
Core lc = LinphoneManager.getLc();
if (lc.getCallsNb() > 0) {
Call call = lc.getCurrentCall();
if (call == null) {
call = lc.getCalls()[0];
}
if (call == null) return;
DisplayMetrics metrics = new DisplayMetrics();
getActivity().getWindowManager().getDefaultDisplay().getMetrics(metrics);
int screenHeight = metrics.heightPixels;
int maxHeight = screenHeight / 4; // Let's take at most 1/4 of the screen for the camera preview
VideoDefinition videoSize = call.getCurrentParams().getSentVideoDefinition(); // It already takes care of rotation
int width = videoSize.getWidth();
int height = videoSize.getHeight();
Log.d("Video height is " + height + ", width is " + width);
width = width * maxHeight / height;
height = maxHeight;
mCaptureView.getHolder().setFixedSize(width, height);
Log.d("Video preview size set to " + width + "x" + height);
}
}
private void fixZOrder(SurfaceView video, SurfaceView preview) {
video.setZOrderOnTop(false);
preview.setZOrderOnTop(true);
preview.setZOrderMediaOverlay(true); // Needed to be able to display control layout over
}
public void switchCamera() {
try {
String currentDevice = LinphoneManager.getLc().getVideoDevice();
String[] devices = LinphoneManager.getLc().getVideoDevicesList();
int index = 0;
for (String d : devices) {
if (d.equals(currentDevice)) {
break;
}
index++;
}
String newDevice;
if (index == 1)
newDevice = devices[0];
else
newDevice = devices[1];
LinphoneManager.getLc().setVideoDevice(newDevice);
CallManager.getInstance().updateCall();
} catch (ArithmeticException ae) {
Log.e("Cannot swtich camera : no camera");
}
}
@Override
public void onResume() {
super.onResume();
if (LinphonePreferences.instance().isOverlayEnabled()) {
LinphoneService.instance().destroyOverlay();
}
if (androidVideoWindowImpl != null) {
synchronized (androidVideoWindowImpl) {
LinphoneManager.getLc().setNativeVideoWindowId(androidVideoWindowImpl);
}
}
mGestureDetector = new GestureDetector(inCallActivity, this);
mScaleDetector = Compatibility.getScaleGestureDetector(inCallActivity, this);
resizePreview();
}
@Override
public void onPause() {
if (androidVideoWindowImpl != null) {
synchronized (androidVideoWindowImpl) {
/*
* this call will destroy native opengl renderer which is used by
* androidVideoWindowImpl
*/
LinphoneManager.getLc().setNativeVideoWindowId(null);
}
}
if (LinphonePreferences.instance().isOverlayEnabled()) {
LinphoneService.instance().createOverlay();
}
super.onPause();
}
public boolean onScale(CompatibilityScaleGestureDetector detector) {
mZoomFactor *= detector.getScaleFactor();
// Don't let the object get too small or too large.
// Zoom to make the video fill the screen vertically
float portraitZoomFactor = ((float) mVideoView.getHeight()) / (float) ((3 * mVideoView.getWidth()) / 4);
// Zoom to make the video fill the screen horizontally
float landscapeZoomFactor = ((float) mVideoView.getWidth()) / (float) ((3 * mVideoView.getHeight()) / 4);
mZoomFactor = Math.max(0.1f, Math.min(mZoomFactor, Math.max(portraitZoomFactor, landscapeZoomFactor)));
Call currentCall = LinphoneManager.getLc().getCurrentCall();
if (currentCall != null) {
currentCall.zoom(mZoomFactor, mZoomCenterX, mZoomCenterY);
return true;
}
return false;
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
if (LinphoneUtils.isCallEstablished(LinphoneManager.getLc().getCurrentCall())) {
if (mZoomFactor > 1) {
// Video is zoomed, slide is used to change center of zoom
if (distanceX > 0 && mZoomCenterX < 1) {
mZoomCenterX += 0.01;
} else if(distanceX < 0 && mZoomCenterX > 0) {
mZoomCenterX -= 0.01;
}
if (distanceY < 0 && mZoomCenterY < 1) {
mZoomCenterY += 0.01;
} else if(distanceY > 0 && mZoomCenterY > 0) {
mZoomCenterY -= 0.01;
}
if (mZoomCenterX > 1)
mZoomCenterX = 1;
if (mZoomCenterX < 0)
mZoomCenterX = 0;
if (mZoomCenterY > 1)
mZoomCenterY = 1;
if (mZoomCenterY < 0)
mZoomCenterY = 0;
LinphoneManager.getLc().getCurrentCall().zoom(mZoomFactor, mZoomCenterX, mZoomCenterY);
return true;
}
}
return false;
}
@Override
public boolean onDoubleTap(MotionEvent e) {
if (LinphoneUtils.isCallEstablished(LinphoneManager.getLc().getCurrentCall())) {
if (mZoomFactor == 1.f) {
// Zoom to make the video fill the screen vertically
float portraitZoomFactor = ((float) mVideoView.getHeight()) / (float) ((3 * mVideoView.getWidth()) / 4);
// Zoom to make the video fill the screen horizontally
float landscapeZoomFactor = ((float) mVideoView.getWidth()) / (float) ((3 * mVideoView.getHeight()) / 4);
mZoomFactor = Math.max(portraitZoomFactor, landscapeZoomFactor);
}
else {
resetZoom();
}
LinphoneManager.getLc().getCurrentCall().zoom(mZoomFactor, mZoomCenterX, mZoomCenterY);
return true;
}
return false;
}
private void resetZoom() {
mZoomFactor = 1.f;
mZoomCenterX = mZoomCenterY = 0.5f;
}
@Override
public void onDestroy() {
inCallActivity = null;
mCaptureView = null;
if (mVideoView != null) {
mVideoView.setOnTouchListener(null);
mVideoView = null;
}
if (androidVideoWindowImpl != null) {
// Prevent linphone from crashing if correspondent hang up while you are rotating
androidVideoWindowImpl.release();
androidVideoWindowImpl = null;
}
if (mGestureDetector != null) {
mGestureDetector.setOnDoubleTapListener(null);
mGestureDetector = null;
}
if (mScaleDetector != null) {
mScaleDetector.destroy();
mScaleDetector = null;
}
super.onDestroy();
}
@Override
public boolean onDown(MotionEvent e) {
return true; // Needed to make the GestureDetector working
}
@Override
public boolean onDoubleTapEvent(MotionEvent e) {
return false;
}
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
return false;
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
return false;
}
@Override
public void onLongPress(MotionEvent e) {
}
@Override
public void onShowPress(MotionEvent e) {
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
return false;
}
}