/* * 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 surfaces = new ArrayList(); 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()); } } }