Skip to content

Commit

Permalink
feat(core): add way to scale the preview view inside its parent
Browse files Browse the repository at this point in the history
It also fixes issue with the aspect ratio and the preview
  • Loading branch information
ThibaultBee committed Feb 28, 2024
1 parent 70cb3ab commit 994b995
Show file tree
Hide file tree
Showing 11 changed files with 425 additions and 127 deletions.
9 changes: 7 additions & 2 deletions core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,15 @@ dependencies {
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation "androidx.core:core-ktx:${androidxCoreVersion}"
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3'
implementation 'androidx.test:rules:1.5.0'

implementation 'androidx.camera:camera-viewfinder:1.4.0-alpha04'
implementation 'com.google.guava:guava:31.0.1-jre'

testImplementation 'androidx.test:rules:1.5.0'
testImplementation 'junit:junit:4.13.2'
testImplementation "io.mockk:mockk:1.12.2"
testImplementation 'io.mockk:mockk:1.12.2'

androidTestImplementation 'androidx.test:rules:1.5.0'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,10 @@ val Size.isPortrait: Boolean
* Check if the size is in landscape orientation.
*/
val Size.isLandscape: Boolean
get() = !isPortrait
get() = !isPortrait

/**
* Find the closest size to the given size in a list of sizes.
*/
fun List<Size>.closestTo(size: Size): Size =
this.minBy { abs((it.width * it.height) - (size.width * size.height)) }
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ import io.github.thibaultbee.streampack.listeners.OnErrorListener
import io.github.thibaultbee.streampack.streamers.helpers.CameraStreamerConfigurationHelper
import io.github.thibaultbee.streampack.streamers.interfaces.ICameraStreamer
import io.github.thibaultbee.streampack.streamers.settings.BaseCameraStreamerSettings
import io.github.thibaultbee.streampack.views.AutoFitSurfaceView
import io.github.thibaultbee.streampack.views.PreviewView
import kotlinx.coroutines.runBlocking

Expand Down Expand Up @@ -89,7 +88,7 @@ open class BaseCameraStreamer(
*
* Inside, it launches both camera and microphone capture.
*
* @param previewSurface Where to display camera capture. Could be a [Surface] from a [PreviewView], an [AutoFitSurfaceView], a [SurfaceView] or a [TextureView].
* @param previewSurface Where to display camera capture. Could be a [Surface] from a [SurfaceView] or a [TextureView].
* @param cameraId camera id (get camera id list from [Context.cameraList])
*
* @throws [StreamPackError] if audio or video capture couldn't be launch
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import kotlin.math.roundToInt
* A [SurfaceView] that can be adjusted to a specified aspect ratio and
* performs center-crop transformation of input frames.
*/
@Deprecated("Use PreviewView instead.", ReplaceWith("PreviewView"))
open class AutoFitSurfaceView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,65 +16,30 @@
*/
package io.github.thibaultbee.streampack.views

import android.graphics.Point
import android.hardware.camera2.CameraCharacteristics
import android.hardware.camera2.params.StreamConfigurationMap
import android.util.Size
import android.view.Display
import kotlin.math.max
import kotlin.math.min

/** Helper class used to pre-compute shortest and longest sides of a [Size] */
class SmartSize(width: Int, height: Int) {
var size = Size(width, height)
var long = max(size.width, size.height)
var short = min(size.width, size.height)
override fun toString() = "SmartSize(${long}x${short})"
}

/** Standard High Definition size for pictures and video */
val SIZE_1080P: SmartSize = SmartSize(1920, 1080)

/** Returns a [SmartSize] object for the given [Display] */
fun getDisplaySmartSize(display: Display): SmartSize {
val outPoint = Point()
display.getRealSize(outPoint)
return SmartSize(outPoint.x, outPoint.y)
}
import io.github.thibaultbee.streampack.internal.utils.extensions.closestTo

/**
* Returns the largest available PREVIEW size. For more information, see:
* https://d.android.com/reference/android/hardware/camera2/CameraDevice and
* https://developer.android.com/reference/android/hardware/camera2/params/StreamConfigurationMap
*/
fun <T> getPreviewOutputSize(
display: Display,
characteristics: CameraCharacteristics,
targetSize: Size,
targetClass: Class<T>,
format: Int? = null
): Size {

// Find which is smaller: screen or 1080p
val screenSize = getDisplaySmartSize(display)
val hdScreen = screenSize.long >= SIZE_1080P.long || screenSize.short >= SIZE_1080P.short
val maxSize = if (hdScreen) SIZE_1080P else screenSize

// If image format is provided, use it to determine supported sizes; else use target class
val config = characteristics.get(
CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP
)!!
if (format == null)
assert(StreamConfigurationMap.isOutputSupportedFor(targetClass))
else
assert(config.isOutputSupportedFor(format))
val allSizes = if (format == null)
config.getOutputSizes(targetClass) else config.getOutputSizes(format)
val allSizes =
characteristics[CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP]!!.getOutputSizes(
targetClass
).toList()

// Get available sizes and sort them by area from largest to smallest
val validSizes = allSizes
.sortedWith(compareBy { it.height * it.width })
.map { SmartSize(it.width, it.height) }.reversed()
.map { Size(it.width, it.height) }.reversed()

// Then, get the largest output size that is smaller or equal than our max size
return validSizes.first { it.long <= maxSize.long && it.short <= maxSize.short }.size
}
return validSizes.closestTo(targetSize)
}
Loading

0 comments on commit 994b995

Please sign in to comment.