From b6f09d1a2fb2b7dcf97a3f00412bc9b34c2e48ff Mon Sep 17 00:00:00 2001 From: pedroSG94 Date: Tue, 10 Sep 2024 11:54:01 +0200 Subject: [PATCH 1/2] add camerauvcsource to rotation example --- app/build.gradle.kts | 3 +- .../streamer/rotation/CameraUvcSource.kt | 88 +++++++++++++++++++ .../pedro/streamer/rotation/CameraXSource.kt | 2 +- .../streamer/rotation/RotationActivity.kt | 27 ++++++ app/src/main/res/menu/rotation_menu.xml | 4 + app/src/main/res/values/strings.xml | 1 + gradle/libs.versions.toml | 2 + 7 files changed, 125 insertions(+), 2 deletions(-) create mode 100644 app/src/main/java/com/pedro/streamer/rotation/CameraUvcSource.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 258f78ca7..e36c43f4b 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -9,7 +9,7 @@ android { defaultConfig { applicationId = "com.pedro.streamer" - minSdk = 16 + minSdk = 19 targetSdk = 34 versionCode = libs.versions.versionCode.get().toInt() versionName = libs.versions.versionName.get() @@ -34,6 +34,7 @@ android { } dependencies { + implementation(libs.uvcandroid) implementation(project(":library")) implementation(libs.androidx.constraintlayout) implementation(libs.androidx.appcompat) diff --git a/app/src/main/java/com/pedro/streamer/rotation/CameraUvcSource.kt b/app/src/main/java/com/pedro/streamer/rotation/CameraUvcSource.kt new file mode 100644 index 000000000..771d2c20d --- /dev/null +++ b/app/src/main/java/com/pedro/streamer/rotation/CameraUvcSource.kt @@ -0,0 +1,88 @@ +/* + * + * * Copyright (C) 2024 pedroSG94. + * * + * * Licensed under the Apache License, Version 2.0 (the "License"); + * * you may not use this file except in compliance with the License. + * * You may obtain a copy of the License at + * * + * * http://www.apache.org/licenses/LICENSE-2.0 + * * + * * Unless required by applicable law or agreed to in writing, software + * * distributed under the License is distributed on an "AS IS" BASIS, + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * See the License for the specific language governing permissions and + * * limitations under the License. + * + */ + +package com.pedro.streamer.rotation + +import android.graphics.SurfaceTexture +import android.hardware.usb.UsbDevice +import android.view.Surface +import com.herohan.uvcapp.CameraHelper +import com.herohan.uvcapp.ICameraHelper +import com.pedro.library.util.sources.video.VideoSource + + +/** + * Created by pedro on 10/9/24. + */ +class CameraUvcSource: VideoSource() { + + private var cameraHelper: ICameraHelper? = null + private var running = false + private var surface: Surface? = null + + override fun create(width: Int, height: Int, fps: Int, rotation: Int): Boolean { + return true + } + + override fun start(surfaceTexture: SurfaceTexture) { + this.surfaceTexture = surfaceTexture + surface = Surface(surfaceTexture) + cameraHelper = CameraHelper() + cameraHelper?.setStateCallback(stateCallback) + running = true + } + + override fun stop() { + surface?.let { cameraHelper?.removeSurface(it) } + surface?.release() + surface = null + cameraHelper?.release() + cameraHelper = null + running = false + } + + override fun release() { + } + + override fun isRunning(): Boolean = running + + private val stateCallback: ICameraHelper.StateCallback = object : ICameraHelper.StateCallback { + override fun onAttach(device: UsbDevice) { + cameraHelper?.selectDevice(device) + } + + override fun onDeviceOpen(device: UsbDevice, isFirstOpen: Boolean) { + cameraHelper?.openCamera() + } + + override fun onCameraOpen(device: UsbDevice) { + cameraHelper?.startPreview() + surface?.let { cameraHelper?.addSurface(it, false) } + } + + override fun onCameraClose(device: UsbDevice) { + + } + + override fun onDeviceClose(device: UsbDevice) {} + + override fun onDetach(device: UsbDevice) {} + + override fun onCancel(device: UsbDevice) {} + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pedro/streamer/rotation/CameraXSource.kt b/app/src/main/java/com/pedro/streamer/rotation/CameraXSource.kt index fbecadd1c..0fa0d8223 100644 --- a/app/src/main/java/com/pedro/streamer/rotation/CameraXSource.kt +++ b/app/src/main/java/com/pedro/streamer/rotation/CameraXSource.kt @@ -118,5 +118,5 @@ class CameraXSource( } } - override fun getLifecycle(): Lifecycle = lifecycleRegistry + override val lifecycle: Lifecycle = lifecycleRegistry } \ No newline at end of file diff --git a/app/src/main/java/com/pedro/streamer/rotation/RotationActivity.kt b/app/src/main/java/com/pedro/streamer/rotation/RotationActivity.kt index 630a426e3..fdc59554c 100644 --- a/app/src/main/java/com/pedro/streamer/rotation/RotationActivity.kt +++ b/app/src/main/java/com/pedro/streamer/rotation/RotationActivity.kt @@ -24,11 +24,14 @@ import android.view.MenuItem import android.view.MotionEvent import android.view.View import android.view.View.OnTouchListener +import android.view.WindowManager import androidx.annotation.RequiresApi import androidx.appcompat.app.AppCompatActivity +import com.pedro.encoder.input.video.CameraHelper import com.pedro.library.util.sources.audio.MicrophoneSource import com.pedro.library.util.sources.video.Camera1Source import com.pedro.library.util.sources.video.Camera2Source +import com.pedro.library.view.OrientationForced import com.pedro.streamer.R import com.pedro.streamer.utils.FilterMenu import com.pedro.streamer.utils.setColor @@ -73,19 +76,28 @@ class RotationActivity : AppCompatActivity(), OnTouchListener { R.id.video_source_camera1 -> { currentVideoSource = updateMenuColor(currentVideoSource, item) cameraFragment.genericStream.changeVideoSource(Camera1Source(applicationContext)) + updateOrientation(false) } R.id.video_source_camera2 -> { currentVideoSource = updateMenuColor(currentVideoSource, item) cameraFragment.genericStream.changeVideoSource(Camera2Source(applicationContext)) + updateOrientation(false) } R.id.video_source_camerax -> { currentVideoSource = updateMenuColor(currentVideoSource, item) cameraFragment.genericStream.changeVideoSource(CameraXSource(applicationContext)) + updateOrientation(false) + } + R.id.video_source_camera_uvc -> { + currentVideoSource = updateMenuColor(currentVideoSource, item) + cameraFragment.genericStream.changeVideoSource(CameraUvcSource()) + updateOrientation(true) } R.id.video_source_bitmap -> { currentVideoSource = updateMenuColor(currentVideoSource, item) val bitmap = BitmapFactory.decodeResource(resources, R.mipmap.ic_launcher) cameraFragment.genericStream.changeVideoSource(BitmapSource(bitmap)) + updateOrientation(false) } R.id.audio_source_microphone -> { currentAudioSource = updateMenuColor(currentAudioSource, item) @@ -125,4 +137,19 @@ class RotationActivity : AppCompatActivity(), OnTouchListener { item.setColor(this, R.color.appColor) return item } + + private fun updateOrientation(isUvc: Boolean) { + //UVC cameras can't adapt orientation depend of the device orientation so we need force use always landscape orientations + if (isUvc) { + cameraFragment.genericStream.getGlInterface().forceOrientation(OrientationForced.LANDSCAPE) + cameraFragment.genericStream.getGlInterface().autoHandleOrientation = false + cameraFragment.genericStream.getGlInterface().setCameraOrientation(0) + } else { //Reset orientation to the correct orientation depend of device orientation + cameraFragment.genericStream.getGlInterface().forceOrientation(OrientationForced.NONE) + cameraFragment.genericStream.getGlInterface().autoHandleOrientation = true + val orientation = CameraHelper.getCameraOrientation(this) + cameraFragment.genericStream.getGlInterface().setCameraOrientation(if (orientation == 0) 270 else orientation - 90) + cameraFragment.genericStream.getGlInterface().setIsPortrait(CameraHelper.isPortrait(this)) + } + } } \ No newline at end of file diff --git a/app/src/main/res/menu/rotation_menu.xml b/app/src/main/res/menu/rotation_menu.xml index d4d84c9c3..793cc2a06 100644 --- a/app/src/main/res/menu/rotation_menu.xml +++ b/app/src/main/res/menu/rotation_menu.xml @@ -14,6 +14,10 @@ android:title="@string/camerax" android:id="@+id/video_source_camerax" /> + Camera1 Camera2 CameraX + CameraUVC Bitmap diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 1d164d1c7..bc913b202 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -18,6 +18,7 @@ annotation = "1.8.2" coroutines = "1.8.1" junit = "4.13.2" mockito = "5.4.0" +uvcandroid = "1.0.7" [libraries] androidx-annotation = { module = "androidx.annotation:annotation", version.ref = "annotation" } @@ -31,6 +32,7 @@ junit = { module = "junit:junit", version.ref = "junit" } kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "coroutines" } kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "coroutines" } mockito-kotlin = { module = "org.mockito.kotlin:mockito-kotlin", version.ref = "mockito" } +uvcandroid = { module = "com.herohan:UVCAndroid", version.ref = "uvcandroid" } [plugins] android-application = { id = "com.android.application", version.ref = "agp" } From 94fa53a1e557a0cc56ebe3116aea0e22d953d084 Mon Sep 17 00:00:00 2001 From: pedroSG94 Date: Fri, 13 Sep 2024 01:37:20 +0200 Subject: [PATCH 2/2] set optimal resolution to camerauvcsource --- .../pedro/streamer/rotation/CameraUvcSource.kt | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/app/src/main/java/com/pedro/streamer/rotation/CameraUvcSource.kt b/app/src/main/java/com/pedro/streamer/rotation/CameraUvcSource.kt index 771d2c20d..c19e3c7ff 100644 --- a/app/src/main/java/com/pedro/streamer/rotation/CameraUvcSource.kt +++ b/app/src/main/java/com/pedro/streamer/rotation/CameraUvcSource.kt @@ -20,15 +20,21 @@ package com.pedro.streamer.rotation import android.graphics.SurfaceTexture import android.hardware.usb.UsbDevice +import android.os.Build import android.view.Surface +import androidx.annotation.RequiresApi import com.herohan.uvcapp.CameraHelper import com.herohan.uvcapp.ICameraHelper +import com.pedro.encoder.input.video.Camera2ResolutionCalculator import com.pedro.library.util.sources.video.VideoSource +import com.serenegiant.usb.Size +import kotlin.math.abs /** * Created by pedro on 10/9/24. */ +@RequiresApi(Build.VERSION_CODES.LOLLIPOP) class CameraUvcSource: VideoSource() { private var cameraHelper: ICameraHelper? = null @@ -72,6 +78,8 @@ class CameraUvcSource: VideoSource() { override fun onCameraOpen(device: UsbDevice) { cameraHelper?.startPreview() + val resolution = getOptimalResolution() + if (resolution != null) cameraHelper?.previewSize = resolution surface?.let { cameraHelper?.addSurface(it, false) } } @@ -85,4 +93,12 @@ class CameraUvcSource: VideoSource() { override fun onCancel(device: UsbDevice) {} } + + private fun getOptimalResolution(): Size? { + val supportedSizes = cameraHelper?.supportedSizeList ?: return null + val supportedResolutions = supportedSizes.map { android.util.Size(it.width, it.height) }.toTypedArray() + val resolution = Camera2ResolutionCalculator.getOptimalResolution(android.util.Size(width, height), supportedResolutions) + val validSizes = supportedSizes.filter { it.width == resolution.width && it.height == resolution.height } + return validSizes.minByOrNull { abs(fps - it.fps) } + } } \ No newline at end of file