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