| /* | |
| * 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); | |
| } | |
| } | |
| } |