1 /* 2 * Copyright 2016 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 package org.webrtc; 12 13 import android.media.MediaRecorder; 14 15 /** 16 * Base interface for camera1 and camera2 implementations. Extends VideoCapturer with a 17 * switchCamera() function. Also provides subinterfaces for handling camera events, and a helper 18 * class for detecting camera freezes. 19 */ 20 public interface CameraVideoCapturer extends VideoCapturer { 21 /** 22 * Camera events handler - can be used to be notifed about camera events. The callbacks are 23 * executed from an arbitrary thread. 24 */ 25 public interface CameraEventsHandler { 26 // Camera error handler - invoked when camera can not be opened 27 // or any camera exception happens on camera thread. onCameraError(String errorDescription)28 void onCameraError(String errorDescription); 29 30 // Called when camera is disconnected. onCameraDisconnected()31 void onCameraDisconnected(); 32 33 // Invoked when camera stops receiving frames. onCameraFreezed(String errorDescription)34 void onCameraFreezed(String errorDescription); 35 36 // Callback invoked when camera is opening. onCameraOpening(String cameraName)37 void onCameraOpening(String cameraName); 38 39 // Callback invoked when first camera frame is available after camera is started. onFirstFrameAvailable()40 void onFirstFrameAvailable(); 41 42 // Callback invoked when camera is closed. onCameraClosed()43 void onCameraClosed(); 44 } 45 46 /** 47 * Camera switch handler - one of these functions are invoked with the result of switchCamera(). 48 * The callback may be called on an arbitrary thread. 49 */ 50 public interface CameraSwitchHandler { 51 // Invoked on success. `isFrontCamera` is true if the new camera is front facing. onCameraSwitchDone(boolean isFrontCamera)52 void onCameraSwitchDone(boolean isFrontCamera); 53 54 // Invoked on failure, e.g. camera is stopped or only one camera available. onCameraSwitchError(String errorDescription)55 void onCameraSwitchError(String errorDescription); 56 } 57 58 /** 59 * Switch camera to the next valid camera id. This can only be called while the camera is running. 60 * This function can be called from any thread. 61 */ switchCamera(CameraSwitchHandler switchEventsHandler)62 void switchCamera(CameraSwitchHandler switchEventsHandler); 63 64 /** 65 * Switch camera to the specified camera id. This can only be called while the camera is running. 66 * This function can be called from any thread. 67 */ switchCamera(CameraSwitchHandler switchEventsHandler, String cameraName)68 void switchCamera(CameraSwitchHandler switchEventsHandler, String cameraName); 69 70 /** 71 * MediaRecorder add/remove handler - one of these functions are invoked with the result of 72 * addMediaRecorderToCamera() or removeMediaRecorderFromCamera calls. 73 * The callback may be called on an arbitrary thread. 74 */ 75 @Deprecated 76 public interface MediaRecorderHandler { 77 // Invoked on success. onMediaRecorderSuccess()78 void onMediaRecorderSuccess(); 79 80 // Invoked on failure, e.g. camera is stopped or any exception happens. onMediaRecorderError(String errorDescription)81 void onMediaRecorderError(String errorDescription); 82 } 83 84 /** 85 * Add MediaRecorder to camera pipeline. This can only be called while the camera is running. 86 * Once MediaRecorder is added to camera pipeline camera switch is not allowed. 87 * This function can be called from any thread. 88 */ 89 @Deprecated addMediaRecorderToCamera( MediaRecorder mediaRecorder, MediaRecorderHandler resultHandler)90 default void addMediaRecorderToCamera( 91 MediaRecorder mediaRecorder, MediaRecorderHandler resultHandler) { 92 throw new UnsupportedOperationException("Deprecated and not implemented."); 93 } 94 95 /** 96 * Remove MediaRecorder from camera pipeline. This can only be called while the camera is running. 97 * This function can be called from any thread. 98 */ 99 @Deprecated removeMediaRecorderFromCamera(MediaRecorderHandler resultHandler)100 default void removeMediaRecorderFromCamera(MediaRecorderHandler resultHandler) { 101 throw new UnsupportedOperationException("Deprecated and not implemented."); 102 } 103 104 /** 105 * Helper class to log framerate and detect if the camera freezes. It will run periodic callbacks 106 * on the SurfaceTextureHelper thread passed in the ctor, and should only be operated from that 107 * thread. 108 */ 109 public static class CameraStatistics { 110 private final static String TAG = "CameraStatistics"; 111 private final static int CAMERA_OBSERVER_PERIOD_MS = 2000; 112 private final static int CAMERA_FREEZE_REPORT_TIMOUT_MS = 4000; 113 114 private final SurfaceTextureHelper surfaceTextureHelper; 115 private final CameraEventsHandler eventsHandler; 116 private int frameCount; 117 private int freezePeriodCount; 118 // Camera observer - monitors camera framerate. Observer is executed on camera thread. 119 private final Runnable cameraObserver = new Runnable() { 120 @Override 121 public void run() { 122 final int cameraFps = Math.round(frameCount * 1000.0f / CAMERA_OBSERVER_PERIOD_MS); 123 Logging.d(TAG, "Camera fps: " + cameraFps + "."); 124 if (frameCount == 0) { 125 ++freezePeriodCount; 126 if (CAMERA_OBSERVER_PERIOD_MS * freezePeriodCount >= CAMERA_FREEZE_REPORT_TIMOUT_MS 127 && eventsHandler != null) { 128 Logging.e(TAG, "Camera freezed."); 129 if (surfaceTextureHelper.isTextureInUse()) { 130 // This can only happen if we are capturing to textures. 131 eventsHandler.onCameraFreezed("Camera failure. Client must return video buffers."); 132 } else { 133 eventsHandler.onCameraFreezed("Camera failure."); 134 } 135 return; 136 } 137 } else { 138 freezePeriodCount = 0; 139 } 140 frameCount = 0; 141 surfaceTextureHelper.getHandler().postDelayed(this, CAMERA_OBSERVER_PERIOD_MS); 142 } 143 }; 144 CameraStatistics( SurfaceTextureHelper surfaceTextureHelper, CameraEventsHandler eventsHandler)145 public CameraStatistics( 146 SurfaceTextureHelper surfaceTextureHelper, CameraEventsHandler eventsHandler) { 147 if (surfaceTextureHelper == null) { 148 throw new IllegalArgumentException("SurfaceTextureHelper is null"); 149 } 150 this.surfaceTextureHelper = surfaceTextureHelper; 151 this.eventsHandler = eventsHandler; 152 this.frameCount = 0; 153 this.freezePeriodCount = 0; 154 surfaceTextureHelper.getHandler().postDelayed(cameraObserver, CAMERA_OBSERVER_PERIOD_MS); 155 } 156 checkThread()157 private void checkThread() { 158 if (Thread.currentThread() != surfaceTextureHelper.getHandler().getLooper().getThread()) { 159 throw new IllegalStateException("Wrong thread"); 160 } 161 } 162 addFrame()163 public void addFrame() { 164 checkThread(); 165 ++frameCount; 166 } 167 release()168 public void release() { 169 surfaceTextureHelper.getHandler().removeCallbacks(cameraObserver); 170 } 171 } 172 } 173