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