blob: e9db5d30659fc115b16f4660dd66efc8670b0ced [file] [log] [blame]
/*
* Copyright (C) 2014 The Android Open Source Project
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.android.server.telecom.testapps;
import com.android.ex.camera2.blocking.BlockingCameraManager;
import com.android.ex.camera2.blocking.BlockingCameraManager.BlockingOpenException;
import com.android.ex.camera2.blocking.BlockingSessionCallback;
import com.android.server.telecom.testapps.R;
import com.android.server.telecom.testapps.TestConnectionService.TestConnection;
import android.content.Context;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CaptureFailure;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Handler;
import android.telecom.Connection;
import android.telecom.VideoProfile;
import android.telecom.VideoProfile.CameraCapabilities;
import android.text.TextUtils;
import android.util.Log;
import android.util.Size;
import android.view.Surface;
import java.lang.IllegalArgumentException;
import java.lang.String;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
/**
* Implements the VideoCallProvider.
*/
public class TestVideoProvider extends Connection.VideoProvider {
private TestConnection mConnection;
private CameraCapabilities mCameraCapabilities;
private Random random;
private Surface mDisplaySurface;
private Surface mPreviewSurface;
private Context mContext;
/** Used to play incoming video during a call. */
private MediaPlayer mIncomingMediaPlayer;
private CameraManager mCameraManager;
private CameraDevice mCameraDevice;
private CameraCaptureSession mCameraSession;
private CameraThread mLooperThread;
private final Handler mHandler = new Handler();
private String mCameraId;
private static final long SESSION_TIMEOUT_MS = 2000;
public TestVideoProvider(Context context, TestConnection connection) {
mConnection = connection;
mContext = context;
random = new Random();
mCameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
}
@Override
public void onSetCamera(String cameraId) {
log("Set camera to " + cameraId);
mCameraId = cameraId;
stopCamera();
// Get the capabilities of the camera
setCameraCapabilities(mCameraId);
}
@Override
public void onSetPreviewSurface(Surface surface) {
log("Set preview surface " + (surface == null ? "unset" : "set"));
if (mPreviewSurface != null) {
stopCamera();
}
mPreviewSurface = surface;
if (!TextUtils.isEmpty(mCameraId) && mPreviewSurface != null) {
startCamera(mCameraId);
}
}
@Override
public void onSetDisplaySurface(Surface surface) {
log("Set display surface " + (surface == null ? "unset" : "set"));
mDisplaySurface = surface;
if (mDisplaySurface != null) {
if (mIncomingMediaPlayer == null) {
// For a Rick-Rolling good time use R.raw.test_video
mIncomingMediaPlayer = createMediaPlayer(mDisplaySurface, R.raw.test_pattern);
}
mIncomingMediaPlayer.setSurface(mDisplaySurface);
if (!mIncomingMediaPlayer.isPlaying()) {
mIncomingMediaPlayer.start();
}
} else {
if (mIncomingMediaPlayer != null) {
mIncomingMediaPlayer.stop();
mIncomingMediaPlayer.setSurface(null);
}
}
}
@Override
public void onSetDeviceOrientation(int rotation) {
log("Set device orientation " + rotation);
}
/**
* Sets the zoom value, creating a new CallCameraCapabalities object. If the zoom value is
* non-positive, assume that zoom is not supported.
*/
@Override
public void onSetZoom(float value) {
log("Set zoom to " + value);
}
/**
* "Sends" a request with a video call profile. Assumes that this response succeeds and sends
* the response back via the CallVideoClient.
*/
@Override
public void onSendSessionModifyRequest(final VideoProfile fromProfile,
final VideoProfile requestProfile) {
log("Sent session modify request");
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
final VideoProfile responseProfile = new VideoProfile(
requestProfile.getVideoState(), requestProfile.getQuality());
mConnection.setVideoState(requestProfile.getVideoState());
receiveSessionModifyResponse(
SESSION_MODIFY_REQUEST_SUCCESS,
requestProfile,
responseProfile);
}
}, 2000);
}
@Override
public void onSendSessionModifyResponse(VideoProfile responseProfile) {
}
/**
* Returns a CallCameraCapabilities object without supporting zoom.
*/
@Override
public void onRequestCameraCapabilities() {
log("Requested camera capabilities");
changeCameraCapabilities(mCameraCapabilities);
}
/**
* Randomly reports data usage of value ranging from 10MB to 60MB.
*/
@Override
public void onRequestConnectionDataUsage() {
log("Requested connection data usage");
long dataUsageKb = (10 *1024) + random.nextInt(50 * 1024);
changeCallDataUsage(dataUsageKb);
}
/**
* We do not have a need to set a paused image.
*/
@Override
public void onSetPauseImage(Uri uri) {
// Not implemented.
}
/**
* Stop and cleanup the media players used for test video playback.
*/
public void stopAndCleanupMedia() {
if (mIncomingMediaPlayer != null) {
mIncomingMediaPlayer.setSurface(null);
mIncomingMediaPlayer.stop();
mIncomingMediaPlayer.release();
mIncomingMediaPlayer = null;
}
}
private static void log(String msg) {
Log.w("TestCallVideoProvider", "[TestCallServiceProvider] " + msg);
}
/**
* Creates a media player to play a video resource on a surface.
* @param surface The surface.
* @param videoResource The video resource.
* @return The {@code MediaPlayer}.
*/
private MediaPlayer createMediaPlayer(Surface surface, int videoResource) {
MediaPlayer mediaPlayer = MediaPlayer.create(mContext.getApplicationContext(),
videoResource);
mediaPlayer.setSurface(surface);
mediaPlayer.setLooping(true);
return mediaPlayer;
}
/**
* Starts displaying the camera image on the preview surface.
*
* @param cameraId
*/
private void startCamera(String cameraId) {
stopCamera();
if (mPreviewSurface == null) {
return;
}
// Configure a looper thread.
mLooperThread = new CameraThread();
Handler mHandler;
try {
mHandler = mLooperThread.start();
} catch (Exception e) {
log("Exception: " + e);
return;
}
// Get the camera device.
try {
BlockingCameraManager blockingCameraManager = new BlockingCameraManager(mCameraManager);
mCameraDevice = blockingCameraManager.openCamera(cameraId, null /* listener */,
mHandler);
} catch (CameraAccessException e) {
log("CameraAccessException: " + e);
return;
} catch (BlockingOpenException be) {
log("BlockingOpenException: " + be);
return;
}
// Create a capture session to get the preview and display it on the surface.
List<Surface> surfaces = new ArrayList<Surface>();
surfaces.add(mPreviewSurface);
CaptureRequest.Builder mCaptureRequest = null;
try {
BlockingSessionCallback blkSession = new BlockingSessionCallback();
mCameraDevice.createCaptureSession(surfaces, blkSession, mHandler);
mCaptureRequest = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
mCaptureRequest.addTarget(mPreviewSurface);
mCameraSession = blkSession.waitAndGetSession(SESSION_TIMEOUT_MS);
} catch (CameraAccessException e) {
log("CameraAccessException: " + e);
return;
}
// Keep repeating
try {
mCameraSession.setRepeatingRequest(mCaptureRequest.build(), new CameraCaptureCallback(),
mHandler);
} catch (CameraAccessException e) {
log("CameraAccessException: " + e);
return;
}
}
/**
* Stops the camera and looper thread.
*/
public void stopCamera() {
try {
if (mCameraDevice != null) {
mCameraDevice.close();
mCameraDevice = null;
}
if (mLooperThread != null) {
mLooperThread.close();
mLooperThread = null;
}
} catch (Exception e) {
log("stopCamera Exception: "+e.toString());
}
}
/**
* Required listener for camera capture events.
*/
private class CameraCaptureCallback extends CameraCaptureSession.CaptureCallback {
@Override
public void onCaptureCompleted(CameraCaptureSession camera, CaptureRequest request,
TotalCaptureResult result) {
}
@Override
public void onCaptureFailed(CameraCaptureSession camera, CaptureRequest request,
CaptureFailure failure) {
}
}
/**
* Uses the camera manager to retrieve the camera capabilities for the chosen camera.
*
* @param cameraId The camera ID to get the capabilities for.
*/
private void setCameraCapabilities(String cameraId) {
CameraManager cameraManager = (CameraManager) mContext.getSystemService(
Context.CAMERA_SERVICE);
CameraCharacteristics c = null;
try {
c = cameraManager.getCameraCharacteristics(cameraId);
} catch (IllegalArgumentException | CameraAccessException e) {
// Ignoring camera problems.
}
if (c != null) {
// Get the video size for the camera
StreamConfigurationMap map = c.get(
CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
Size previewSize = map.getOutputSizes(SurfaceTexture.class)[0];
mCameraCapabilities = new CameraCapabilities(previewSize.getWidth(),
previewSize.getHeight());
}
}
}