Skip to content

Instantly share code, notes, and snippets.

@naveenrobo
Created December 23, 2021 13:49
Show Gist options
  • Save naveenrobo/7ab5137778d84ed826267da759cf7eb5 to your computer and use it in GitHub Desktop.
Save naveenrobo/7ab5137778d84ed826267da759cf7eb5 to your computer and use it in GitHub Desktop.
import android.annotation.TargetApi
import android.content.Context
import android.graphics.ImageFormat
import android.hardware.Camera
import android.hardware.camera2.CameraCharacteristics
import android.hardware.camera2.CameraManager
import android.hardware.camera2.CameraMetadata
import android.os.Build
import android.util.Log
import com.twilio.video.Camera2Capturer
import com.twilio.video.CameraCapturer
import com.twilio.video.VideoCapturer
import dev.dotworld.mdm.utils.Utils
import tvi.webrtc.Camera1Enumerator
import tvi.webrtc.CapturerObserver
import tvi.webrtc.SurfaceTextureHelper
import java.util.*
/*
* Simple wrapper class that uses Camera2Capturer with supported devices.
*/
class CameraCapturerCompat(context: Context, cameraId: String) : VideoCapturer {
private val camera1Capturer: CameraCapturer?
private val camera2Capturer: Camera2Capturer?
private val activeCapturer: VideoCapturer
private val camera1IdMap: MutableMap<Source, String> = EnumMap(Source::class.java)
private val camera1SourceMap: MutableMap<String, Source> = HashMap()
enum class Source {
FRONT_CAMERA, BACK_CAMERA
}
override fun initialize(
surfaceTextureHelper: SurfaceTextureHelper,
context: Context,
capturerObserver: CapturerObserver
) {
activeCapturer.initialize(surfaceTextureHelper, context, capturerObserver)
}
override fun startCapture(width: Int, height: Int, framerate: Int) {
activeCapturer.startCapture(width, height, framerate)
}
@Throws(InterruptedException::class)
override fun stopCapture() {
activeCapturer.stopCapture()
}
override fun isScreencast(): Boolean {
return activeCapturer.isScreencast
}
override fun dispose() {
activeCapturer.dispose()
}
private fun usingCamera1(): Boolean {
return camera1Capturer != null
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private fun isCameraIdSupported(context: Context, cameraId: String): Boolean {
var isMonoChromeSupported = false
var isPrivateImageFormatSupported = false
val cameraManager = context.getSystemService(Context.CAMERA_SERVICE) as CameraManager
val cameraCharacteristics: CameraCharacteristics = try {
cameraManager.getCameraCharacteristics(cameraId)
} catch (e: Exception) {
e.printStackTrace()
return false
}
/*
* This is a temporary work around for a RuntimeException that occurs on devices which contain cameras
* that do not support ImageFormat.PRIVATE output formats. A long term fix is currently in development.
* https://github.com/twilio/video-quickstart-android/issues/431
*/
val streamMap =
cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)
if (streamMap != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
isPrivateImageFormatSupported = streamMap.isOutputSupportedFor(ImageFormat.PRIVATE)
}
/*
* Read the color filter arrangements of the camera to filter out the ones that support
* SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_MONO or SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_NIR.
* Visit this link for details on supported values - https://developer.android.com/reference/android/hardware/camera2/CameraCharacteristics#SENSOR_INFO_COLOR_FILTER_ARRANGEMENT
*/
val colorFilterArrangement = cameraCharacteristics.get(
CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT
)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && colorFilterArrangement != null) {
isMonoChromeSupported = (colorFilterArrangement
== CameraMetadata.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_MONO ||
colorFilterArrangement
== CameraMetadata.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_NIR)
}
return isPrivateImageFormatSupported && !isMonoChromeSupported
}
init {
if (Camera2Capturer.isSupported(context) && isCameraIdSupported(
context,
cameraId
) && Utils.isLollipopApiSupported
) {
Log.i(TAG, "Camera 2: ")
camera2Capturer = Camera2Capturer(context, cameraId)
activeCapturer = camera2Capturer
camera1Capturer = null
} else {
Log.i(TAG, "Camera 1")
setCamera1Maps()
camera1Capturer = CameraCapturer(context, camera1IdMap[getCameraSourceByCameraId(cameraId)]!!)
activeCapturer = camera1Capturer
camera2Capturer = null
}
}
private fun getCameraSourceByCameraId(cameraId: String): Source? {
for (i in 0 until Camera.getNumberOfCameras()) {
val cameraInfo = Camera.CameraInfo()
Camera.getCameraInfo(i, cameraInfo)
if (cameraId.contains(i.toString())) {
return if (cameraInfo.facing == 1) {
Source.FRONT_CAMERA
} else {
Source.BACK_CAMERA
}
}
}
return null
}
private fun setCamera1Maps() {
val camera1Enumerator = Camera1Enumerator()
for (deviceName in camera1Enumerator.deviceNames) {
if (camera1Enumerator.isFrontFacing(deviceName)) {
camera1IdMap[Source.FRONT_CAMERA] = deviceName
camera1SourceMap[deviceName] = Source.FRONT_CAMERA
}
if (camera1Enumerator.isBackFacing(deviceName)) {
camera1IdMap[Source.BACK_CAMERA] = deviceName
camera1SourceMap[deviceName] = Source.BACK_CAMERA
}
}
}
companion object {
private const val TAG = "CameraCapturerCompat"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment