Skip to content

Commit

Permalink
Aperture: Hook up video dynamic range
Browse files Browse the repository at this point in the history
Change-Id: Ia7bf123cfb6afca962e195c991b8919f985c1f49
  • Loading branch information
SebaUbuntu committed Oct 3, 2023
1 parent 4d1eedf commit b349c8a
Show file tree
Hide file tree
Showing 12 changed files with 265 additions and 13 deletions.
8 changes: 8 additions & 0 deletions LICENSES/CC-PDDC.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@

The person or persons who have associated work with this document (the "Dedicator" or "Certifier") hereby either (a) certifies that, to the best of his knowledge, the work of authorship identified is in the public domain of the country from which the work is published, or (b) hereby dedicates whatever copyright the dedicators holds in the work of authorship identified below (the "Work") to the public domain. A certifier, moreover, dedicates any copyright interest he may have in the associated work, and for these purposes, is described as a "dedicator" below.

A certifier has taken reasonable steps to verify the copyright status of this work. Certifier recognizes that his good faith efforts may not shield him from liability if in fact the work certified is not in the public domain.

Dedicator makes this dedication for the benefit of the public at large and to the detriment of the Dedicator's heirs and successors. Dedicator intends this dedication to be an overt act of relinquishment in perpetuity of all present and future rights under copyright law, whether vested or contingent, in the Work. Dedicator understands that such relinquishment of all rights includes the relinquishment of all rights to enforce (by lawsuit or otherwise) those copyrights in the Work.

Dedicator recognizes that, once placed in the public domain, the Work may be freely reproduced, distributed, transmitted, used, modified, built upon, or otherwise exploited by anyone for any purpose, commercial or non-commercial, and in any way, including by methods that have not yet been invented or conceived.
59 changes: 53 additions & 6 deletions app/src/main/java/org/lineageos/aperture/CameraActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ import org.lineageos.aperture.camera.FrameRate
import org.lineageos.aperture.camera.HotPixelMode
import org.lineageos.aperture.camera.NoiseReductionMode
import org.lineageos.aperture.camera.ShadingMode
import org.lineageos.aperture.camera.VideoDynamicRange
import org.lineageos.aperture.camera.VideoQualityInfo
import org.lineageos.aperture.camera.VideoStabilizationMode
import org.lineageos.aperture.ext.*
import org.lineageos.aperture.qr.QrImageAnalyzer
Expand Down Expand Up @@ -173,6 +175,7 @@ open class CameraActivity : AppCompatActivity() {
private val videoFrameRateButton by lazy { findViewById<Button>(R.id.videoFrameRateButton) }
private val videoQualityButton by lazy { findViewById<Button>(R.id.videoQualityButton) }
private val videoRecordingStateButton by lazy { findViewById<ImageButton>(R.id.videoRecordingStateButton) }
private val videoDynamicRangeButton by lazy { findViewById<Button>(R.id.videoDynamicRangeButton) }
private val viewFinder by lazy { findViewById<PreviewView>(R.id.viewFinder) }
private val viewFinderFocus by lazy { findViewById<ImageView>(R.id.viewFinderFocus) }
private val zoomLevel by lazy { findViewById<HorizontalSlider>(R.id.zoomLevel) }
Expand Down Expand Up @@ -211,6 +214,7 @@ open class CameraActivity : AppCompatActivity() {
private var photoEffect by nonNullablePropertyDelegate { model.photoEffect }
private var videoQuality by nonNullablePropertyDelegate { model.videoQuality }
private var videoFrameRate by nullablePropertyDelegate { model.videoFrameRate }
private var videoDynamicRange by nonNullablePropertyDelegate { model.videoDynamicRange }
private var videoMicMode by nonNullablePropertyDelegate { model.videoMicMode }
private var videoRecording by nullablePropertyDelegate { model.videoRecording }
private var videoDuration by nonNullablePropertyDelegate { model.videoRecordingDuration }
Expand All @@ -228,10 +232,12 @@ open class CameraActivity : AppCompatActivity() {
// Video
private val supportedVideoQualities: Set<Quality>
get() = camera.supportedVideoQualities.keys
private val videoQualityInfo: VideoQualityInfo?
get() = camera.supportedVideoQualities[videoQuality]
private val supportedVideoFrameRates: Set<FrameRate>
get() = camera.supportedVideoQualities.getOrDefault(
videoQuality, setOf()
)
get() = videoQualityInfo?.supportedFrameRates ?: setOf()
private val supportedVideoDynamicRanges: Set<VideoDynamicRange>
get() = videoQualityInfo?.supportedDynamicRanges ?: setOf()
private lateinit var videoAudioConfig: AudioConfig

// QR
Expand Down Expand Up @@ -573,6 +579,7 @@ open class CameraActivity : AppCompatActivity() {
photoEffect = sharedPreferences.photoEffect
videoQuality = sharedPreferences.videoQuality
videoFrameRate = sharedPreferences.videoFrameRate
videoDynamicRange = sharedPreferences.videoDynamicRange
videoMicMode = sharedPreferences.lastMicMode

// Handle intent
Expand Down Expand Up @@ -627,6 +634,7 @@ open class CameraActivity : AppCompatActivity() {
aspectRatioButton.setOnClickListener { cycleAspectRatio() }
videoQualityButton.setOnClickListener { cycleVideoQuality() }
videoFrameRateButton.setOnClickListener { cycleVideoFrameRate() }
videoDynamicRangeButton.setOnClickListener { cycleVideoDynamicRange() }
effectButton.setOnClickListener { cyclePhotoEffects() }
gridButton.setOnClickListener { cycleGridMode() }
timerButton.setOnClickListener { toggleTimerMode() }
Expand Down Expand Up @@ -868,6 +876,7 @@ open class CameraActivity : AppCompatActivity() {
aspectRatioButton.isVisible = cameraMode != CameraMode.VIDEO
videoQualityButton.isVisible = cameraMode == CameraMode.VIDEO
videoFrameRateButton.isVisible = cameraMode == CameraMode.VIDEO
videoDynamicRangeButton.isVisible = cameraMode == CameraMode.VIDEO
micButton.isVisible = cameraMode == CameraMode.VIDEO

updateSecondaryTopBarButtons()
Expand Down Expand Up @@ -1086,6 +1095,16 @@ open class CameraActivity : AppCompatActivity() {
} ?: resources.getString(R.string.video_framerate_auto)
}

model.videoDynamicRange.observe(this) {
val videoDynamicRange = it

// Update secondary bar buttons
videoDynamicRangeButton.setCompoundDrawablesWithIntrinsicBounds(
0, videoDynamicRange.icon, 0, 0
)
videoDynamicRangeButton.setText(videoDynamicRange.title)
}

// Observe video mic mode
model.videoMicMode.observe(this) {
val videoMicMode = it ?: return@observe
Expand Down Expand Up @@ -1536,6 +1555,12 @@ open class CameraActivity : AppCompatActivity() {
videoFrameRate ?: FrameRate.FPS_30, supportedVideoFrameRates
)

// Set video dynamic range
videoDynamicRange = videoDynamicRange.takeIf {
supportedVideoDynamicRanges.contains(it)
} ?: supportedVideoDynamicRanges.first()
cameraController.videoCaptureDynamicRange = videoDynamicRange.dynamicRange

CameraController.VIDEO_CAPTURE
}
}
Expand Down Expand Up @@ -1862,9 +1887,9 @@ open class CameraActivity : AppCompatActivity() {
val videoRecording = model.videoRecording.value

val supportedVideoQualities = camera.supportedVideoQualities
val supportedVideoFrameRates = supportedVideoQualities.getOrDefault(
videoQuality, setOf()
)
val videoQualityInfo = supportedVideoQualities[videoQuality]
val supportedVideoFrameRates = videoQualityInfo?.supportedFrameRates ?: setOf()
val supportedVideoDynamicRanges = videoQualityInfo?.supportedDynamicRanges ?: setOf()

flashButton.isEnabled =
cameraMode != CameraMode.PHOTO || cameraState == CameraState.IDLE
Expand All @@ -1875,6 +1900,8 @@ open class CameraActivity : AppCompatActivity() {
cameraState == CameraState.IDLE && supportedVideoQualities.size > 1
videoFrameRateButton.isEnabled =
cameraState == CameraState.IDLE && supportedVideoFrameRates.size > 1
videoDynamicRangeButton.isEnabled =
cameraState == CameraState.IDLE && supportedVideoDynamicRanges.size > 1
micButton.isEnabled =
cameraState == CameraState.IDLE || videoRecording?.isAudioSourceConfigured == true
}
Expand Down Expand Up @@ -1953,6 +1980,26 @@ open class CameraActivity : AppCompatActivity() {
bindCameraUseCases()
}

private fun cycleVideoDynamicRange() {
if (!canRestartCamera()) {
return
}

val currentVideoDynamicRange = videoDynamicRange
val newVideoDynamicRange =
supportedVideoDynamicRanges.toList().sorted().next(currentVideoDynamicRange)

if (newVideoDynamicRange == currentVideoDynamicRange) {
return
}

videoDynamicRange = newVideoDynamicRange

sharedPreferences.videoDynamicRange = videoDynamicRange

bindCameraUseCases()
}

/**
* Set the specified grid mode, also updating the icon
*/
Expand Down
28 changes: 23 additions & 5 deletions app/src/main/java/org/lineageos/aperture/camera/Camera.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import android.os.Build
import androidx.camera.camera2.interop.Camera2CameraInfo
import androidx.camera.core.CameraInfo
import androidx.camera.core.CameraSelector
import androidx.camera.core.DynamicRange
import androidx.camera.video.Recorder
import org.lineageos.aperture.ext.*
import kotlin.reflect.safeCast
Expand Down Expand Up @@ -49,11 +48,30 @@ class Camera(cameraInfo: CameraInfo, cameraManager: CameraManager) {
private val supportedVideoFrameRates = cameraInfo.supportedFrameRateRanges.mapNotNull {
FrameRate.fromRange(it)
}.toSet()

private val videoCapabilities = Recorder.getVideoCapabilities(cameraInfo)

private val supportedVideoDynamicRanges = videoCapabilities.supportedDynamicRanges.map {
VideoDynamicRange.fromDynamicRange(it)
}

private val videoQualityForDynamicRanges = supportedVideoDynamicRanges.associateWith {
videoCapabilities.getSupportedQualities(it.dynamicRange)
}

val supportedVideoQualities =
Recorder.getVideoCapabilities(cameraInfo).getSupportedQualities(DynamicRange.SDR)
.associateWith {
supportedVideoFrameRates + cameraManager.getAdditionalVideoFrameRates(cameraId, it)
}.toMap()
videoQualityForDynamicRanges.values.flatten().toSet().associateWith {
VideoQualityInfo(
it,
supportedVideoFrameRates.plus(
cameraManager.getAdditionalVideoFrameRates(cameraId, it)
),
videoQualityForDynamicRanges.entries.filter { dynamicRangeToQualities ->
dynamicRangeToQualities.value.contains(it)
}.map { dynamicRangeToQualities -> dynamicRangeToQualities.key }.toSet()
)
}

val supportsVideoRecording = supportedVideoQualities.isNotEmpty()

val supportedExtensionModes = cameraManager.extensionsManager.getSupportedModes(cameraSelector)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,11 @@ class CameraViewModel : ViewModel() {
*/
val videoFrameRate = MutableLiveData<FrameRate?>()

/**
* Video dynamic range.
*/
val videoDynamicRange = MutableLiveData<VideoDynamicRange>()

/**
* Video mic mode.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* SPDX-FileCopyrightText: 2022-2023 The LineageOS Project
* SPDX-License-Identifier: Apache-2.0
*/

package org.lineageos.aperture.camera

import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import androidx.camera.core.DynamicRange
import org.lineageos.aperture.R

/**
* Video dynamic range.
* @param dynamicRange The [DynamicRange] it refers to
* @param title A string resource used to represent the dynamic range
* @param icon An icon resource used to represent the dynamic range
*/
enum class VideoDynamicRange(
val dynamicRange: DynamicRange,
@StringRes val title: Int,
@DrawableRes val icon: Int,
) {
SDR(
DynamicRange.SDR,
R.string.video_dynamic_range_sdr,
R.drawable.ic_hdr_off,
),
HLG_10_BIT(
DynamicRange.HLG_10_BIT,
R.string.video_dynamic_range_hlg_10_bit,
R.drawable.ic_hdr_on,
),
HDR10_10_BIT(
DynamicRange.HDR10_10_BIT,
R.string.video_dynamic_range_hdr10_10_bit,
R.drawable.ic_hdr_on,
),
HDR10_PLUS_10_BIT(
DynamicRange.HDR10_PLUS_10_BIT,
R.string.video_dynamic_range_hdr10_plus_10_bit,
R.drawable.ic_hdr_off,
),
DOLBY_VISION_10_BIT(
DynamicRange.DOLBY_VISION_10_BIT,
R.string.video_dynamic_range_dolby_vision_10_bit,
R.drawable.ic_dolby,
),
DOLBY_VISION_8_BIT(
DynamicRange.DOLBY_VISION_8_BIT,
R.string.video_dynamic_range_dolby_vision_8_bit,
R.drawable.ic_dolby,
);

companion object {
fun fromDynamicRange(dynamicRange: DynamicRange) = values().first {
it.dynamicRange == dynamicRange
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* SPDX-FileCopyrightText: 2023 The LineageOS Project
* SPDX-License-Identifier: Apache-2.0
*/

package org.lineageos.aperture.camera

import androidx.camera.video.Quality

/**
* Video [Quality] info.
* @param quality The quality
* @param supportedFrameRates The supported frame rates for this quality
* @param supportedDynamicRanges The supported dynamic ranges for this quality
*/
data class VideoQualityInfo(
val quality: Quality,
val supportedFrameRates: Set<FrameRate>,
val supportedDynamicRanges: Set<VideoDynamicRange>,
)
28 changes: 28 additions & 0 deletions app/src/main/java/org/lineageos/aperture/ext/SharedPreferences.kt
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import org.lineageos.aperture.camera.FrameRate
import org.lineageos.aperture.camera.HotPixelMode
import org.lineageos.aperture.camera.NoiseReductionMode
import org.lineageos.aperture.camera.ShadingMode
import org.lineageos.aperture.camera.VideoDynamicRange
import org.lineageos.aperture.utils.GestureActions
import org.lineageos.aperture.utils.GridMode
import org.lineageos.aperture.utils.TimerMode
Expand Down Expand Up @@ -445,3 +446,30 @@ internal var SharedPreferences.forceTorchHelpShown: Boolean
set(value) = edit {
putBoolean(FORCE_TORCH_HELP_SHOWN_KEY, value)
}

// Video dynamic range
private const val VIDEO_DYNAMIC_RANGE_KEY = "video_dynamic_range"
private const val VIDEO_DYNAMIC_RANGE_DEFAULT = "sdr"
internal var SharedPreferences.videoDynamicRange: VideoDynamicRange
get() = when (getString(VIDEO_DYNAMIC_RANGE_KEY, VIDEO_DYNAMIC_RANGE_DEFAULT)) {
"sdr" -> VideoDynamicRange.SDR
"hlg_10_bit" -> VideoDynamicRange.HLG_10_BIT
"hdr10_10_bit" -> VideoDynamicRange.HDR10_10_BIT
"hdr10_plus_10_bit" -> VideoDynamicRange.HDR10_PLUS_10_BIT
"dolby_vision_10_bit" -> VideoDynamicRange.DOLBY_VISION_10_BIT
"dolby_vision_8_bit" -> VideoDynamicRange.DOLBY_VISION_8_BIT
// Default to sdr
else -> VideoDynamicRange.SDR
}
set(value) = edit {
putString(
VIDEO_DYNAMIC_RANGE_KEY, when (value) {
VideoDynamicRange.SDR -> "sdr"
VideoDynamicRange.HLG_10_BIT -> "hlg_10_bit"
VideoDynamicRange.HDR10_10_BIT -> "hdr10_10_bit"
VideoDynamicRange.HDR10_PLUS_10_BIT -> "hdr10_plus_10_bit"
VideoDynamicRange.DOLBY_VISION_10_BIT -> "dolby_vision_10_bit"
VideoDynamicRange.DOLBY_VISION_8_BIT -> "dolby_vision_8_bit"
}
)
}
18 changes: 18 additions & 0 deletions app/src/main/res/drawable/ic_dolby.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
SPDX-FileCopyrightText: Dolby Laboratories Inc.
SPDX-License-Identifier: CC-PDDC
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="279.08dp"
android:height="193.28dp"
android:tint="#000000"
android:viewportWidth="279.08"
android:viewportHeight="193.28">
<path
android:fillColor="@android:color/white"
android:pathData="m279.08,193.28h-28.14c-53.82,0 -96.64,-44.04 -96.64,-96.64C154.31,44.04 198.35,0 250.95,0h28.14z" />
<path
android:fillColor="@android:color/white"
android:pathData="m0,0h28.14c53.82,0 96.64,44.04 96.64,96.64 0,52.6 -44.04,96.64 -96.64,96.64H0Z" />
</vector>
15 changes: 15 additions & 0 deletions app/src/main/res/drawable/ic_hdr_off.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
SPDX-FileCopyrightText: Material Design Authors / Google LLC
SPDX-License-Identifier: Apache-2.0
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="#000000"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="@android:color/white"
android:pathData="M792,904L56,168L112,112L848,846L792,904ZM780,600L744,520L700,520L700,586L640,526L640,360L780,360Q804,360 822,378Q840,396 840,420L840,460Q840,478 829.5,492.5Q819,507 804,516L840,600L780,600ZM700,460L780,460Q780,460 780,460Q780,460 780,460L780,420Q780,420 780,420Q780,420 780,420L700,420L700,460ZM580,466L474,360L520,360Q544,360 562,378Q580,396 580,420L580,466ZM120,600L120,360L180,360L180,440L260,440L260,360L320,360L320,600L260,600L260,500L180,500L180,600L120,600ZM380,436L440,496L440,540L485,540Q485,540 485,540Q485,540 485,540L540,596Q535,598 530,599Q525,600 520,600L380,600L380,436Z" />
</vector>
15 changes: 15 additions & 0 deletions app/src/main/res/drawable/ic_hdr_on.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
SPDX-FileCopyrightText: Material Design Authors / Google LLC
SPDX-License-Identifier: Apache-2.0
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="#000000"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M21,11.5v-1c0,-0.8 -0.7,-1.5 -1.5,-1.5L16,9v6h1.5v-2h1.1l0.9,2L21,15l-0.9,-2.1c0.5,-0.3 0.9,-0.8 0.9,-1.4zM19.5,11.5h-2v-1h2v1zM6.5,11h-2L4.5,9L3,9v6h1.5v-2.5h2L6.5,15L8,15L8,9L6.5,9v2zM13,9L9.5,9v6L13,15c0.8,0 1.5,-0.7 1.5,-1.5v-3c0,-0.8 -0.7,-1.5 -1.5,-1.5zM13,13.5h-2v-3h2v3z" />
</vector>
Loading

0 comments on commit b349c8a

Please sign in to comment.