<commit_before>/*
 * libjingle
 * Copyright 2015 Google Inc.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  1. Redistributions of source code must retain the above copyright notice,
 *     this list of conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright notice,
 *     this list of conditions and the following disclaimer in the documentation
 *     and/or other materials provided with the distribution.
 *  3. The name of the author may not be used to endorse or promote products
 *     derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package org.webrtc;

import android.content.Context;
import android.graphics.SurfaceTexture;
import android.hardware.Camera;
import android.hardware.Camera.PreviewCallback;
import android.opengl.GLES11Ext;
import android.opengl.GLES20;
import android.os.Handler;
import android.os.Looper;
import android.os.SystemClock;
import android.util.Log;
import android.view.Surface;
import android.view.WindowManager;

import org.json.JSONException;

import org.webrtc.CameraEnumerationAndroid.CaptureFormat;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Exchanger;
import java.util.concurrent.TimeUnit;

// Android specific implementation of VideoCapturer.
// An instance of this class can be created by an application using
// VideoCapturerAndroid.create();
// This class extends VideoCapturer with a method to easily switch between the
// front and back camera. It also provides methods for enumerating valid device
// names.
//
// Threading notes: this class is called from C++ code, and from Camera
// Java callbacks.  Since these calls happen on different threads,
// the entry points to this class are all synchronized.  This shouldn't present
// a performance bottleneck because only onPreviewFrame() is called more than
// once (and is called serially on a single thread), so the lock should be
// uncontended.  Note that each of these synchronized methods must check
// |camera| for null to account for having possibly waited for stopCapture() to
// complete.
@SuppressWarnings("deprecation")
public class VideoCapturerAndroid extends VideoCapturer implements PreviewCallback {
  private final static String TAG = "VideoCapturerAndroid";
  private final static int CAMERA_OBSERVER_PERIOD_MS = 5000;

  private Camera camera;  // Only non-null while capturing.
  private CameraThread cameraThread;
  private Handler cameraThreadHandler;
  private Context applicationContext;
  private int id;
  private Camera.CameraInfo info;
  private SurfaceTexture cameraSurfaceTexture;
  private int cameraGlTexture = 0;
  private final FramePool videoBuffers = new FramePool();
  // Remember the requested format in case we want to switch cameras.
  private int requestedWidth;
  private int requestedHeight;
  private int requestedFramerate;
  // The capture format will be the closest supported format to the requested format.
  private CaptureFormat captureFormat;
  private int cameraFramesCount;
  private int captureBuffersCount;
  private volatile boolean pendingCameraSwitch;
  private CapturerObserver frameObserver = null;
  private CameraErrorHandler errorHandler = null;

  // Camera error callback.
  private final Camera.ErrorCallback cameraErrorCallback =
      new Camera.ErrorCallback() {
    @Override
    public void onError(int error, Camera camera) {
      String errorMessage;
      if (error == android.hardware.Camera.CAMERA_ERROR_SERVER_DIED) {
        errorMessage = "Camera server died!";
      } else {
        errorMessage = "Camera error: " + error;
      }
      Log.e(TAG, errorMessage);
      if (errorHandler != null) {
        errorHandler.onCameraError(errorMessage);
      }
    }
  };

  // Camera observer - monitors camera framerate and amount of available
  // camera buffers. Observer is excecuted on camera thread.
  private final Runnable cameraObserver = new Runnable() {
    @Override
    public void run() {
      int cameraFps = (cameraFramesCount * 1000 + CAMERA_OBSERVER_PERIOD_MS / 2)
          / CAMERA_OBSERVER_PERIOD_MS;
      double averageCaptureBuffersCount = 0;
      if (cameraFramesCount > 0) {
        averageCaptureBuffersCount =
            (double)captureBuffersCount / cameraFramesCount;
      }
      Log.d(TAG, "Camera fps: " + cameraFps + ". CaptureBuffers: " +
          String.format("%.1f", averageCaptureBuffersCount) +
          ". Pending buffers: " + videoBuffers.pendingFramesTimeStamps());
      if (cameraFramesCount == 0) {
        Log.e(TAG, "Camera freezed.");
        if (errorHandler != null) {
          errorHandler.onCameraError("Camera failure.");
        }
      } else {
        cameraFramesCount = 0;
        captureBuffersCount = 0;
        if (cameraThreadHandler != null) {
          cameraThreadHandler.postDelayed(this, CAMERA_OBSERVER_PERIOD_MS);
        }
      }
    }
  };

  // Camera error handler - invoked when camera stops receiving frames
  // or any camera exception happens on camera thread.
  public static interface CameraErrorHandler {
    public void onCameraError(String errorDescription);
  }

  public static VideoCapturerAndroid create(String name,
      CameraErrorHandler errorHandler) {
    VideoCapturer capturer = VideoCapturer.create(name);
    if (capturer != null) {
      VideoCapturerAndroid capturerAndroid = (VideoCapturerAndroid) capturer;
      capturerAndroid.errorHandler = errorHandler;
      return capturerAndroid;
    }
    return null;
  }

  // Switch camera to the next valid camera id. This can only be called while
  // the camera is running.
  // Returns true on success. False if the next camera does not support the
  // current resolution.
  public synchronized boolean switchCamera(final Runnable switchDoneEvent) {
    if (Camera.getNumberOfCameras() < 2 )
      return false;

    if (cameraThread == null) {
      Log.e(TAG, "Camera has not been started");
      return false;
    }
    if (pendingCameraSwitch) {
      // Do not handle multiple camera switch request to avoid blocking
      // camera thread by handling too many switch request from a queue.
      Log.w(TAG, "Ignoring camera switch request.");
      return false;
    }

    pendingCameraSwitch = true;
    id = (id + 1) % Camera.getNumberOfCameras();
    cameraThreadHandler.post(new Runnable() {
      @Override public void run() {
        switchCameraOnCameraThread(switchDoneEvent);
      }
    });
    return true;
  }

  // Requests a new output format from the video capturer. Captured frames
  // by the camera will be scaled/or dropped by the video capturer.
  public synchronized void onOutputFormatRequest(
      final int width, final int height, final int fps) {
    if (cameraThreadHandler == null) {
      Log.e(TAG, "Calling onOutputFormatRequest() for already stopped camera.");<commit_msg>Android: Guard against switching camera on stopped camera<commit_after>/*
 * libjingle
 * Copyright 2015 Google Inc.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  1. Redistributions of source code must retain the above copyright notice,
 *     this list of conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright notice,
 *     this list of conditions and the following disclaimer in the documentation
 *     and/or other materials provided with the distribution.
 *  3. The name of the author may not be used to endorse or promote products
 *     derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package org.webrtc;

import android.content.Context;
import android.graphics.SurfaceTexture;
import android.hardware.Camera;
import android.hardware.Camera.PreviewCallback;
import android.opengl.GLES11Ext;
import android.opengl.GLES20;
import android.os.Handler;
import android.os.Looper;
import android.os.SystemClock;
import android.util.Log;
import android.view.Surface;
import android.view.WindowManager;

import org.json.JSONException;

import org.webrtc.CameraEnumerationAndroid.CaptureFormat;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Exchanger;
import java.util.concurrent.TimeUnit;

// Android specific implementation of VideoCapturer.
// An instance of this class can be created by an application using
// VideoCapturerAndroid.create();
// This class extends VideoCapturer with a method to easily switch between the
// front and back camera. It also provides methods for enumerating valid device
// names.
//
// Threading notes: this class is called from C++ code, and from Camera
// Java callbacks.  Since these calls happen on different threads,
// the entry points to this class are all synchronized.  This shouldn't present
// a performance bottleneck because only onPreviewFrame() is called more than
// once (and is called serially on a single thread), so the lock should be
// uncontended.  Note that each of these synchronized methods must check
// |camera| for null to account for having possibly waited for stopCapture() to
// complete.
@SuppressWarnings("deprecation")
public class VideoCapturerAndroid extends VideoCapturer implements PreviewCallback {
  private final static String TAG = "VideoCapturerAndroid";
  private final static int CAMERA_OBSERVER_PERIOD_MS = 5000;

  private Camera camera;  // Only non-null while capturing.
  private CameraThread cameraThread;
  private Handler cameraThreadHandler;
  private Context applicationContext;
  private int id;
  private Camera.CameraInfo info;
  private SurfaceTexture cameraSurfaceTexture;
  private int cameraGlTexture = 0;
  private final FramePool videoBuffers = new FramePool();
  // Remember the requested format in case we want to switch cameras.
  private int requestedWidth;
  private int requestedHeight;
  private int requestedFramerate;
  // The capture format will be the closest supported format to the requested format.
  private CaptureFormat captureFormat;
  private int cameraFramesCount;
  private int captureBuffersCount;
  private volatile boolean pendingCameraSwitch;
  private CapturerObserver frameObserver = null;
  private CameraErrorHandler errorHandler = null;

  // Camera error callback.
  private final Camera.ErrorCallback cameraErrorCallback =
      new Camera.ErrorCallback() {
    @Override
    public void onError(int error, Camera camera) {
      String errorMessage;
      if (error == android.hardware.Camera.CAMERA_ERROR_SERVER_DIED) {
        errorMessage = "Camera server died!";
      } else {
        errorMessage = "Camera error: " + error;
      }
      Log.e(TAG, errorMessage);
      if (errorHandler != null) {
        errorHandler.onCameraError(errorMessage);
      }
    }
  };

  // Camera observer - monitors camera framerate and amount of available
  // camera buffers. Observer is excecuted on camera thread.
  private final Runnable cameraObserver = new Runnable() {
    @Override
    public void run() {
      int cameraFps = (cameraFramesCount * 1000 + CAMERA_OBSERVER_PERIOD_MS / 2)
          / CAMERA_OBSERVER_PERIOD_MS;
      double averageCaptureBuffersCount = 0;
      if (cameraFramesCount > 0) {
        averageCaptureBuffersCount =
            (double)captureBuffersCount / cameraFramesCount;
      }
      Log.d(TAG, "Camera fps: " + cameraFps + ". CaptureBuffers: " +
          String.format("%.1f", averageCaptureBuffersCount) +
          ". Pending buffers: " + videoBuffers.pendingFramesTimeStamps());
      if (cameraFramesCount == 0) {
        Log.e(TAG, "Camera freezed.");
        if (errorHandler != null) {
          errorHandler.onCameraError("Camera failure.");
        }
      } else {
        cameraFramesCount = 0;
        captureBuffersCount = 0;
        if (cameraThreadHandler != null) {
          cameraThreadHandler.postDelayed(this, CAMERA_OBSERVER_PERIOD_MS);
        }
      }
    }
  };

  // Camera error handler - invoked when camera stops receiving frames
  // or any camera exception happens on camera thread.
  public static interface CameraErrorHandler {
    public void onCameraError(String errorDescription);
  }

  public static VideoCapturerAndroid create(String name,
      CameraErrorHandler errorHandler) {
    VideoCapturer capturer = VideoCapturer.create(name);
    if (capturer != null) {
      VideoCapturerAndroid capturerAndroid = (VideoCapturerAndroid) capturer;
      capturerAndroid.errorHandler = errorHandler;
      return capturerAndroid;
    }
    return null;
  }

  // Switch camera to the next valid camera id. This can only be called while
  // the camera is running.
  // Returns true on success. False if the next camera does not support the
  // current resolution.
  public synchronized boolean switchCamera(final Runnable switchDoneEvent) {
    if (Camera.getNumberOfCameras() < 2 )
      return false;

    if (cameraThreadHandler == null) {
        Log.e(TAG, "Calling switchCamera() for stopped camera.");
        return false;
    }
    if (pendingCameraSwitch) {
      // Do not handle multiple camera switch request to avoid blocking
      // camera thread by handling too many switch request from a queue.
      Log.w(TAG, "Ignoring camera switch request.");
      return false;
    }

    pendingCameraSwitch = true;
    id = (id + 1) % Camera.getNumberOfCameras();
    cameraThreadHandler.post(new Runnable() {
      @Override public void run() {
        switchCameraOnCameraThread(switchDoneEvent);
      }
    });
    return true;
  }

  // Requests a new output format from the video capturer. Captured frames
  // by the camera will be scaled/or dropped by the video capturer.
  public synchronized void onOutputFormatRequest(
      final int width, final int height, final int fps) {
    if (cameraThreadHandler == null) {
      Log.e(TAG, "Calling onOutputFormatRequest() for already stopped camera.");
{'fraction_non_alphanumeric': 0.0718267038256904, 'fraction_numerical': 0.0036736762097795794, 'mean_word_length': 3.604549431321085, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2650916', 'n_tokens_mistral': 4195, 'n_tokens_neox': 4010, 'n_words': 1590}