1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 17 package com.android.server.telecom.testapps; 18 19 import com.android.ex.camera2.blocking.BlockingCameraManager; 20 import com.android.ex.camera2.blocking.BlockingCameraManager.BlockingOpenException; 21 import com.android.ex.camera2.blocking.BlockingSessionCallback; 22 import com.android.server.telecom.tests.R; 23 24 import android.content.Context; 25 import android.graphics.SurfaceTexture; 26 import android.hardware.camera2.CameraAccessException; 27 import android.hardware.camera2.CameraCaptureSession; 28 import android.hardware.camera2.CameraCharacteristics; 29 import android.hardware.camera2.CameraDevice; 30 import android.hardware.camera2.CameraManager; 31 import android.hardware.camera2.CaptureFailure; 32 import android.hardware.camera2.CaptureRequest; 33 import android.hardware.camera2.TotalCaptureResult; 34 import android.hardware.camera2.params.StreamConfigurationMap; 35 import android.media.MediaPlayer; 36 import android.os.Handler; 37 import android.telecom.CameraCapabilities; 38 import android.telecom.Connection; 39 import android.telecom.VideoProfile; 40 import android.text.TextUtils; 41 import android.util.Log; 42 import android.util.Size; 43 import android.view.Surface; 44 45 import java.lang.IllegalArgumentException; 46 import java.lang.String; 47 import java.util.ArrayList; 48 import java.util.List; 49 import java.util.Random; 50 51 /** 52 * Implements the VideoCallProvider. 53 */ 54 public class TestVideoProvider extends Connection.VideoProvider { 55 private CameraCapabilities mCameraCapabilities; 56 private Random random; 57 private Surface mDisplaySurface; 58 private Surface mPreviewSurface; 59 private Context mContext; 60 /** Used to play incoming video during a call. */ 61 private MediaPlayer mIncomingMediaPlayer; 62 63 private CameraManager mCameraManager; 64 private CameraDevice mCameraDevice; 65 private CameraCaptureSession mCameraSession; 66 private CameraThread mLooperThread; 67 68 private String mCameraId; 69 70 private static final long SESSION_TIMEOUT_MS = 2000; 71 72 public TestVideoProvider(Context context) { 73 mContext = context; 74 random = new Random(); 75 mCameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE); 76 } 77 78 @Override 79 public void onSetCamera(String cameraId) { 80 log("Set camera to " + cameraId); 81 mCameraId = cameraId; 82 83 stopCamera(); 84 // Get the capabilities of the camera 85 setCameraCapabilities(mCameraId); 86 } 87 88 @Override 89 public void onSetPreviewSurface(Surface surface) { 90 log("Set preview surface " + (surface == null ? "unset" : "set")); 91 if (mPreviewSurface != null) { 92 stopCamera(); 93 } 94 95 mPreviewSurface = surface; 96 97 if (!TextUtils.isEmpty(mCameraId) && mPreviewSurface != null) { 98 startCamera(mCameraId); 99 } 100 } 101 102 @Override 103 public void onSetDisplaySurface(Surface surface) { 104 log("Set display surface " + (surface == null ? "unset" : "set")); 105 mDisplaySurface = surface; 106 107 if (mDisplaySurface != null) { 108 if (mIncomingMediaPlayer == null) { 109 // For a Rick-Rolling good time use R.raw.test_video 110 mIncomingMediaPlayer = createMediaPlayer(mDisplaySurface, R.raw.test_pattern); 111 } 112 mIncomingMediaPlayer.setSurface(mDisplaySurface); 113 if (!mIncomingMediaPlayer.isPlaying()) { 114 mIncomingMediaPlayer.start(); 115 } 116 } else { 117 if (mIncomingMediaPlayer != null) { 118 mIncomingMediaPlayer.stop(); 119 mIncomingMediaPlayer.setSurface(null); 120 } 121 } 122 } 123 124 @Override 125 public void onSetDeviceOrientation(int rotation) { 126 log("Set device orientation " + rotation); 127 } 128 129 /** 130 * Sets the zoom value, creating a new CallCameraCapabalities object. If the zoom value is 131 * non-positive, assume that zoom is not supported. 132 */ 133 @Override 134 public void onSetZoom(float value) { 135 log("Set zoom to " + value); 136 } 137 138 /** 139 * "Sends" a request with a video call profile. Assumes that this response succeeds and sends 140 * the response back via the CallVideoClient. 141 */ 142 @Override 143 public void onSendSessionModifyRequest(VideoProfile requestProfile) { 144 log("Sent session modify request"); 145 146 VideoProfile responseProfile = new VideoProfile( 147 requestProfile.getVideoState(), requestProfile.getQuality()); 148 receiveSessionModifyResponse( 149 SESSION_MODIFY_REQUEST_SUCCESS, 150 requestProfile, 151 responseProfile); 152 } 153 154 @Override 155 public void onSendSessionModifyResponse(VideoProfile responseProfile) { 156 157 } 158 159 /** 160 * Returns a CallCameraCapabilities object without supporting zoom. 161 */ 162 @Override 163 public void onRequestCameraCapabilities() { 164 log("Requested camera capabilities"); 165 changeCameraCapabilities(mCameraCapabilities); 166 } 167 168 /** 169 * Randomly reports data usage of value ranging from 10MB to 60MB. 170 */ 171 @Override 172 public void onRequestConnectionDataUsage() { 173 log("Requested connection data usage"); 174 int dataUsageKb = (10 *1024) + random.nextInt(50 * 1024); 175 changeCallDataUsage(dataUsageKb); 176 } 177 178 /** 179 * We do not have a need to set a paused image. 180 */ 181 @Override 182 public void onSetPauseImage(String uri) { 183 // Not implemented. 184 } 185 186 /** 187 * Stop and cleanup the media players used for test video playback. 188 */ 189 public void stopAndCleanupMedia() { 190 if (mIncomingMediaPlayer != null) { 191 mIncomingMediaPlayer.setSurface(null); 192 mIncomingMediaPlayer.stop(); 193 mIncomingMediaPlayer.release(); 194 mIncomingMediaPlayer = null; 195 } 196 } 197 198 private static void log(String msg) { 199 Log.w("TestCallVideoProvider", "[TestCallServiceProvider] " + msg); 200 } 201 202 /** 203 * Creates a media player to play a video resource on a surface. 204 * @param surface The surface. 205 * @param videoResource The video resource. 206 * @return The {@code MediaPlayer}. 207 */ 208 private MediaPlayer createMediaPlayer(Surface surface, int videoResource) { 209 MediaPlayer mediaPlayer = MediaPlayer.create(mContext.getApplicationContext(), 210 videoResource); 211 mediaPlayer.setSurface(surface); 212 mediaPlayer.setLooping(true); 213 return mediaPlayer; 214 } 215 216 /** 217 * Starts displaying the camera image on the preview surface. 218 * 219 * @param cameraId 220 */ 221 private void startCamera(String cameraId) { 222 stopCamera(); 223 224 if (mPreviewSurface == null) { 225 return; 226 } 227 228 // Configure a looper thread. 229 mLooperThread = new CameraThread(); 230 Handler mHandler; 231 try { 232 mHandler = mLooperThread.start(); 233 } catch (Exception e) { 234 log("Exception: " + e); 235 return; 236 } 237 238 // Get the camera device. 239 try { 240 BlockingCameraManager blockingCameraManager = new BlockingCameraManager(mCameraManager); 241 mCameraDevice = blockingCameraManager.openCamera(cameraId, null /* listener */, 242 mHandler); 243 } catch (CameraAccessException e) { 244 log("CameraAccessException: " + e); 245 return; 246 } catch (BlockingOpenException be) { 247 log("BlockingOpenException: " + be); 248 return; 249 } 250 251 // Create a capture session to get the preview and display it on the surface. 252 List<Surface> surfaces = new ArrayList<Surface>(); 253 surfaces.add(mPreviewSurface); 254 CaptureRequest.Builder mCaptureRequest = null; 255 try { 256 BlockingSessionCallback blkSession = new BlockingSessionCallback(); 257 mCameraDevice.createCaptureSession(surfaces, blkSession, mHandler); 258 mCaptureRequest = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); 259 mCaptureRequest.addTarget(mPreviewSurface); 260 mCameraSession = blkSession.waitAndGetSession(SESSION_TIMEOUT_MS); 261 } catch (CameraAccessException e) { 262 log("CameraAccessException: " + e); 263 return; 264 } 265 266 // Keep repeating 267 try { 268 mCameraSession.setRepeatingRequest(mCaptureRequest.build(), new CameraCaptureCallback(), 269 mHandler); 270 } catch (CameraAccessException e) { 271 log("CameraAccessException: " + e); 272 return; 273 } 274 } 275 276 /** 277 * Stops the camera and looper thread. 278 */ 279 public void stopCamera() { 280 try { 281 if (mCameraDevice != null) { 282 mCameraDevice.close(); 283 mCameraDevice = null; 284 } 285 if (mLooperThread != null) { 286 mLooperThread.close(); 287 mLooperThread = null; 288 } 289 } catch (Exception e) { 290 log("stopCamera Exception: "+e.toString()); 291 } 292 } 293 294 /** 295 * Required listener for camera capture events. 296 */ 297 private class CameraCaptureCallback extends CameraCaptureSession.CaptureCallback { 298 @Override 299 public void onCaptureCompleted(CameraCaptureSession camera, CaptureRequest request, 300 TotalCaptureResult result) { 301 } 302 303 @Override 304 public void onCaptureFailed(CameraCaptureSession camera, CaptureRequest request, 305 CaptureFailure failure) { 306 } 307 } 308 309 /** 310 * Uses the camera manager to retrieve the camera capabilities for the chosen camera. 311 * 312 * @param cameraId The camera ID to get the capabilities for. 313 */ 314 private void setCameraCapabilities(String cameraId) { 315 CameraManager cameraManager = (CameraManager) mContext.getSystemService( 316 Context.CAMERA_SERVICE); 317 318 CameraCharacteristics c = null; 319 try { 320 c = cameraManager.getCameraCharacteristics(cameraId); 321 } catch (IllegalArgumentException | CameraAccessException e) { 322 // Ignoring camera problems. 323 } 324 if (c != null) { 325 // Get the video size for the camera 326 StreamConfigurationMap map = c.get( 327 CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); 328 Size previewSize = map.getOutputSizes(SurfaceTexture.class)[0]; 329 330 mCameraCapabilities = new CameraCapabilities(true, 1.0f, previewSize.getWidth(), 331 previewSize.getHeight()); 332 } 333 } 334 } 335