Skip to content

Commit 04f3fdf

Browse files
authored
Merge pull request #2050 from pedroSG94/ts-changes
Ts changes
2 parents 749929e + cc49870 commit 04f3fdf

13 files changed

Lines changed: 690 additions & 21 deletions

File tree

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
/*
2+
* Copyright (C) 2026 pedroSG94.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.pedro.encoder.input.gl.render.filters.`object`
17+
18+
import android.content.Context
19+
import android.graphics.SurfaceTexture
20+
import android.hardware.display.DisplayManager
21+
import android.hardware.display.VirtualDisplay
22+
import android.opengl.GLES11Ext
23+
import android.opengl.GLES20
24+
import android.os.Build
25+
import android.os.Handler
26+
import android.os.Looper
27+
import android.view.Surface
28+
import android.view.View
29+
import androidx.annotation.LayoutRes
30+
import androidx.annotation.RequiresApi
31+
import com.pedro.encoder.R
32+
import com.pedro.encoder.utils.ViewPresentation
33+
import com.pedro.encoder.utils.gl.GlUtil
34+
35+
/**
36+
* Created by pedro on 18/07/18.
37+
*/
38+
@RequiresApi(api = Build.VERSION_CODES.KITKAT_WATCH)
39+
class ViewSurfaceFilterRender: BaseObjectFilterRender() {
40+
private var surfaceTexture: SurfaceTexture? = null
41+
private var surface: Surface? = null
42+
private var virtualDisplay: VirtualDisplay? = null
43+
private var viewPresentation: ViewPresentation? = null
44+
private var context: Context? = null
45+
private var view: View? = null
46+
private var layout: Int? = null
47+
48+
override fun initGlFilter(context: Context) {
49+
this.context = context
50+
fragment = R.raw.surface_fragment
51+
super.initGlFilter(context)
52+
GlUtil.createExternalTextures(streamObjectTextureId.size, streamObjectTextureId, 0)
53+
surfaceTexture = SurfaceTexture(streamObjectTextureId[0])
54+
surfaceTexture?.setDefaultBufferSize(width, height)
55+
surface = Surface(surfaceTexture)
56+
val displayManager: DisplayManager =
57+
context.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
58+
virtualDisplay = displayManager.createVirtualDisplay(
59+
this.javaClass.name,
60+
width, height, context.resources.displayMetrics.densityDpi,
61+
surface,
62+
DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY
63+
)
64+
startRender()
65+
}
66+
67+
override fun drawFilter() {
68+
surfaceTexture?.updateTexImage()
69+
super.drawFilter()
70+
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, streamObjectTextureId[0])
71+
//Set alpha. 0f if no image loaded.
72+
GLES20.glUniform1f(uAlphaHandle, if (streamObjectTextureId[0] == -1) 0f else alpha)
73+
}
74+
75+
override fun release() {
76+
super.release()
77+
viewPresentation?.dismiss()
78+
viewPresentation = null
79+
virtualDisplay?.release()
80+
surfaceTexture?.release()
81+
surface?.release()
82+
}
83+
84+
/**
85+
* This texture must be renderer using an api called on main thread to avoid possible errors
86+
*/
87+
fun getSurfaceTexture(): SurfaceTexture? {
88+
return surfaceTexture
89+
}
90+
91+
fun setView(view: View?) {
92+
if (view == null) return
93+
layout = null
94+
this.view = view
95+
startRender()
96+
}
97+
98+
fun setView(@LayoutRes layoutId: Int) {
99+
if (layoutId == 0) return
100+
this.view = null
101+
this.layout = layoutId
102+
startRender()
103+
}
104+
105+
private fun startRender() {
106+
Handler(Looper.getMainLooper()).post(Runnable {
107+
if (view == null && layout == 0) return@Runnable
108+
val context = this.context ?: return@Runnable
109+
val virtualDisplay = this.virtualDisplay ?: return@Runnable
110+
viewPresentation?.dismiss()
111+
viewPresentation = ViewPresentation(context, virtualDisplay.display)
112+
view?.let { viewPresentation?.setContentView(it) }
113+
layout?.let { viewPresentation?.setContentView(it) }
114+
viewPresentation?.show()
115+
})
116+
}
117+
}

encoder/src/main/java/com/pedro/encoder/input/video/Camera2ApiManager.kt

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -190,11 +190,34 @@ class Camera2ApiManager(context: Context) : CameraDevice.StateCallback() {
190190

191191
@Throws(IllegalStateException::class, Exception::class)
192192
private fun drawSurface(cameraDevice: CameraDevice, surfaces: List<Surface>): CaptureRequest {
193-
val builderInputSurface = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW)
193+
val builderInputSurface = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD)
194194
for (surface in surfaces) builderInputSurface.addTarget(surface)
195195
builderInputSurface.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO)
196196
val validFps = min(60, fps)
197-
builderInputSurface.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, Range(validFps, validFps))
197+
// Find best FPS range instead of forcing strict [30, 30] which causes HAL duplication stutter
198+
var bestRange = Range(validFps, validFps)
199+
try {
200+
val facing = if (cameraId == "1") Facing.FRONT else Facing.BACK
201+
val supportedRanges = getSupportedFps(null, facing)
202+
203+
// Look for a range that maxes out at our target FPS, but allows dipping to save light (e.g. [24, 30] or [15, 30])
204+
for (range in supportedRanges) {
205+
if (range.upper == validFps && range.lower < validFps) {
206+
// Try to avoid dropping too low (e.g. [15, 30] can be too choppy, prefer [24, 30])
207+
if (range.lower >= 24) {
208+
bestRange = range
209+
break
210+
} else if (bestRange.lower == validFps || range.lower > bestRange.lower) {
211+
bestRange = range
212+
}
213+
}
214+
}
215+
Log.i(TAG, "Selected dynamic FPS range: $bestRange for target $validFps")
216+
} catch (e: Exception) {
217+
Log.e(TAG, "Error finding dynamic FPS range", e)
218+
}
219+
220+
builderInputSurface.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, bestRange)
198221
this.builderInputSurface = builderInputSurface
199222
return builderInputSurface.build()
200223
}
@@ -605,7 +628,12 @@ class Camera2ApiManager(context: Context) : CameraDevice.StateCallback() {
605628
builderInputSurface.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_CANCEL)
606629
builderInputSurface.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_OFF)
607630
applyRequest(builderInputSurface)
608-
if (supportedFocusModes.contains(CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE)) {
631+
// Prefer CONTINUOUS_VIDEO: same smooth AF as CONTINUOUS_PICTURE but defers
632+
// fine adjustments to not interrupt frame delivery (no dropped frames on focus hunt).
633+
if (supportedFocusModes.contains(CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_VIDEO)) {
634+
builderInputSurface.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_VIDEO)
635+
isAutoFocusEnabled = true
636+
} else if (supportedFocusModes.contains(CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE)) {
609637
builderInputSurface.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE)
610638
isAutoFocusEnabled = true
611639
} else if (supportedFocusModes.contains(CaptureRequest.CONTROL_AF_MODE_AUTO)) {

0 commit comments

Comments
 (0)