blob: 88228ab57fcc123e1e2813cfde7aab0a207fb06a [file] [log] [blame]
Magnus Jedverta8eab862016-05-20 17:22:12 +02001/*
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
11package org.webrtc;
12
glaznev37adc5e2017-04-27 13:38:29 -070013import android.media.MediaRecorder;
14
Magnus Jedverta8eab862016-05-20 17:22:12 +020015/**
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 */
20public 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.
28 void onCameraError(String errorDescription);
29
sakalf156bdd2016-10-06 00:59:10 -070030 // Called when camera is disconnected.
31 void onCameraDisconnected();
32
Magnus Jedverta8eab862016-05-20 17:22:12 +020033 // Invoked when camera stops receiving frames.
34 void onCameraFreezed(String errorDescription);
35
36 // Callback invoked when camera is opening.
sakalbc18fc02016-09-14 05:36:20 -070037 void onCameraOpening(String cameraName);
Magnus Jedverta8eab862016-05-20 17:22:12 +020038
39 // Callback invoked when first camera frame is available after camera is started.
40 void onFirstFrameAvailable();
41
42 // Callback invoked when camera is closed.
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.
52 void onCameraSwitchDone(boolean isFrontCamera);
53
54 // Invoked on failure, e.g. camera is stopped or only one camera available.
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 */
62 void switchCamera(CameraSwitchHandler switchEventsHandler);
magjedd5c77ab2017-04-19 08:45:45 -070063
64 /**
Aaron Alaniz415e39d2020-01-09 04:26:04 +000065 * 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 */
68 void switchCamera(CameraSwitchHandler switchEventsHandler, String cameraName);
69
70 /**
glaznev37adc5e2017-04-27 13:38:29 -070071 * 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 */
Magnus Jedvert5ebb82b2018-04-12 16:23:34 +020075 @Deprecated
glaznev37adc5e2017-04-27 13:38:29 -070076 public interface MediaRecorderHandler {
77 // Invoked on success.
78 void onMediaRecorderSuccess();
79
80 // Invoked on failure, e.g. camera is stopped or any exception happens.
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 */
Magnus Jedvert5ebb82b2018-04-12 16:23:34 +020089 @Deprecated
90 default void addMediaRecorderToCamera(
91 MediaRecorder mediaRecorder, MediaRecorderHandler resultHandler) {
92 throw new UnsupportedOperationException("Deprecated and not implemented.");
93 }
glaznev37adc5e2017-04-27 13:38:29 -070094
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 */
Magnus Jedvert5ebb82b2018-04-12 16:23:34 +020099 @Deprecated
100 default void removeMediaRecorderFromCamera(MediaRecorderHandler resultHandler) {
101 throw new UnsupportedOperationException("Deprecated and not implemented.");
102 }
glaznev37adc5e2017-04-27 13:38:29 -0700103
104 /**
magjedd5c77ab2017-04-19 08:45:45 -0700105 * 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
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
157 private void checkThread() {
158 if (Thread.currentThread() != surfaceTextureHelper.getHandler().getLooper().getThread()) {
159 throw new IllegalStateException("Wrong thread");
160 }
161 }
162
163 public void addFrame() {
164 checkThread();
165 ++frameCount;
166 }
167
168 public void release() {
169 surfaceTextureHelper.getHandler().removeCallbacks(cameraObserver);
170 }
171 }
Magnus Jedverta8eab862016-05-20 17:22:12 +0200172}