summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/sdk/android/api/org/webrtc/CameraVideoCapturer.java
blob: ec26868b5c64a291c788645af4110d1c5bf5eacf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
/*
 *  Copyright 2016 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

package org.webrtc;

import android.media.MediaRecorder;

/**
 * Base interface for camera1 and camera2 implementations. Extends VideoCapturer with a
 * switchCamera() function. Also provides subinterfaces for handling camera events, and a helper
 * class for detecting camera freezes.
 */
public interface CameraVideoCapturer extends VideoCapturer {
  /**
   * Camera events handler - can be used to be notifed about camera events. The callbacks are
   * executed from an arbitrary thread.
   */
  public interface CameraEventsHandler {
    // Camera error handler - invoked when camera can not be opened
    // or any camera exception happens on camera thread.
    void onCameraError(String errorDescription);

    // Called when camera is disconnected.
    void onCameraDisconnected();

    // Invoked when camera stops receiving frames.
    void onCameraFreezed(String errorDescription);

    // Callback invoked when camera is opening.
    void onCameraOpening(String cameraName);

    // Callback invoked when first camera frame is available after camera is started.
    void onFirstFrameAvailable();

    // Callback invoked when camera is closed.
    void onCameraClosed();
  }

  /**
   * Camera switch handler - one of these functions are invoked with the result of switchCamera().
   * The callback may be called on an arbitrary thread.
   */
  public interface CameraSwitchHandler {
    // Invoked on success. `isFrontCamera` is true if the new camera is front facing.
    void onCameraSwitchDone(boolean isFrontCamera);

    // Invoked on failure, e.g. camera is stopped or only one camera available.
    void onCameraSwitchError(String errorDescription);
  }

  /**
   * Switch camera to the next valid camera id. This can only be called while the camera is running.
   * This function can be called from any thread.
   */
  void switchCamera(CameraSwitchHandler switchEventsHandler);

  /**
   * Switch camera to the specified camera id. This can only be called while the camera is running.
   * This function can be called from any thread.
   */
  void switchCamera(CameraSwitchHandler switchEventsHandler, String cameraName);

  /**
   * MediaRecorder add/remove handler - one of these functions are invoked with the result of
   * addMediaRecorderToCamera() or removeMediaRecorderFromCamera calls.
   * The callback may be called on an arbitrary thread.
   */
  @Deprecated
  public interface MediaRecorderHandler {
    // Invoked on success.
    void onMediaRecorderSuccess();

    // Invoked on failure, e.g. camera is stopped or any exception happens.
    void onMediaRecorderError(String errorDescription);
  }

  /**
   * Add MediaRecorder to camera pipeline. This can only be called while the camera is running.
   * Once MediaRecorder is added to camera pipeline camera switch is not allowed.
   * This function can be called from any thread.
   */
  @Deprecated
  default void addMediaRecorderToCamera(
      MediaRecorder mediaRecorder, MediaRecorderHandler resultHandler) {
    throw new UnsupportedOperationException("Deprecated and not implemented.");
  }

  /**
   * Remove MediaRecorder from camera pipeline. This can only be called while the camera is running.
   * This function can be called from any thread.
   */
  @Deprecated
  default void removeMediaRecorderFromCamera(MediaRecorderHandler resultHandler) {
    throw new UnsupportedOperationException("Deprecated and not implemented.");
  }

  /**
   * Helper class to log framerate and detect if the camera freezes. It will run periodic callbacks
   * on the SurfaceTextureHelper thread passed in the ctor, and should only be operated from that
   * thread.
   */
  public static class CameraStatistics {
    private final static String TAG = "CameraStatistics";
    private final static int CAMERA_OBSERVER_PERIOD_MS = 2000;
    private final static int CAMERA_FREEZE_REPORT_TIMOUT_MS = 4000;

    private final SurfaceTextureHelper surfaceTextureHelper;
    private final CameraEventsHandler eventsHandler;
    private int frameCount;
    private int freezePeriodCount;
    // Camera observer - monitors camera framerate. Observer is executed on camera thread.
    private final Runnable cameraObserver = new Runnable() {
      @Override
      public void run() {
        final int cameraFps = Math.round(frameCount * 1000.0f / CAMERA_OBSERVER_PERIOD_MS);
        Logging.d(TAG, "Camera fps: " + cameraFps + ".");
        if (frameCount == 0) {
          ++freezePeriodCount;
          if (CAMERA_OBSERVER_PERIOD_MS * freezePeriodCount >= CAMERA_FREEZE_REPORT_TIMOUT_MS
              && eventsHandler != null) {
            Logging.e(TAG, "Camera freezed.");
            if (surfaceTextureHelper.isTextureInUse()) {
              // This can only happen if we are capturing to textures.
              eventsHandler.onCameraFreezed("Camera failure. Client must return video buffers.");
            } else {
              eventsHandler.onCameraFreezed("Camera failure.");
            }
            return;
          }
        } else {
          freezePeriodCount = 0;
        }
        frameCount = 0;
        surfaceTextureHelper.getHandler().postDelayed(this, CAMERA_OBSERVER_PERIOD_MS);
      }
    };

    public CameraStatistics(
        SurfaceTextureHelper surfaceTextureHelper, CameraEventsHandler eventsHandler) {
      if (surfaceTextureHelper == null) {
        throw new IllegalArgumentException("SurfaceTextureHelper is null");
      }
      this.surfaceTextureHelper = surfaceTextureHelper;
      this.eventsHandler = eventsHandler;
      this.frameCount = 0;
      this.freezePeriodCount = 0;
      surfaceTextureHelper.getHandler().postDelayed(cameraObserver, CAMERA_OBSERVER_PERIOD_MS);
    }

    private void checkThread() {
      if (Thread.currentThread() != surfaceTextureHelper.getHandler().getLooper().getThread()) {
        throw new IllegalStateException("Wrong thread");
      }
    }

    public void addFrame() {
      checkThread();
      ++frameCount;
    }

    public void release() {
      surfaceTextureHelper.getHandler().removeCallbacks(cameraObserver);
    }
  }
}