xref: /aosp_15_r20/external/webrtc/sdk/android/api/org/webrtc/CameraVideoCapturer.java (revision d9f758449e529ab9291ac668be2861e7a55c2422)
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