Skip to content

Commit 173d1f6

Browse files
authored
Merge pull request #1944 from chenqinggang001/master
Implement multi-preview surface rendering
2 parents 5273552 + e6c84ff commit 173d1f6

File tree

2 files changed

+113
-0
lines changed

2 files changed

+113
-0
lines changed

library/src/main/java/com/pedro/library/base/StreamBase.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,15 @@ abstract class StreamBase(
366366
if (removeCallbacks) previewCallback.removeCallbacks()
367367
}
368368

369+
fun addPreviewSurface(
370+
surface: Surface,
371+
config: GlStreamInterface.MultiPreviewConfig
372+
) {
373+
if (!surface.isValid) throw IllegalArgumentException("Make sure the Surface is valid")
374+
if (!isOnPreview) throw IllegalStateException("Preview must be started before adding surfaces")
375+
glInterface.addMultiPreviewSurface(surface, config)
376+
}
377+
369378
/**
370379
* Change video source to Camera1 or Camera2.
371380
* Must be called after prepareVideo.

library/src/main/java/com/pedro/library/view/GlStreamInterface.kt

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import com.pedro.encoder.utils.gl.GlUtil
4040
import com.pedro.library.util.Filter
4141
import com.pedro.library.util.SensorRotationManager
4242
import java.util.concurrent.BlockingQueue
43+
import java.util.concurrent.ConcurrentHashMap
4344
import java.util.concurrent.ExecutorService
4445
import java.util.concurrent.LinkedBlockingQueue
4546
import java.util.concurrent.atomic.AtomicBoolean
@@ -59,7 +60,35 @@ class GlStreamInterface(private val context: Context): OnFrameAvailableListener,
5960
private val surfaceManagerEncoderRecord = SurfaceManager()
6061
private val surfaceManagerPhoto = SurfaceManager()
6162
private val surfaceManagerPreview = SurfaceManager()
63+
private val multiPreviewSurfaceManagers = ConcurrentHashMap<Surface, PreviewSurfaceInfo>()
6264
private val mainRender = MainRender()
65+
66+
/**
67+
* User-facing configuration for a multi-preview surface.
68+
* All configuration fields are mutable to support dynamic updates.
69+
*
70+
* @param width the width of the preview. 0 to use preview or encoder resolution
71+
* @param height the height of the preview. 0 to use preview or encoder resolution
72+
* @param horizontalFlip true to flip horizontally
73+
* @param verticalFlip true to flip vertically
74+
* @param aspectRatioMode aspect ratio mode for this surface
75+
* @param isPortrait true for portrait orientation, false for landscape
76+
* @param viewPort viewport for this surface. null for full screen
77+
*/
78+
data class MultiPreviewConfig(
79+
var width: Int = 0,
80+
var height: Int = 0,
81+
var horizontalFlip: Boolean = false,
82+
var verticalFlip: Boolean = false,
83+
var aspectRatioMode: AspectRatioMode = AspectRatioMode.Adjust,
84+
var isPortrait: Boolean = false,
85+
var viewPort: ViewPort? = null
86+
)
87+
88+
data class PreviewSurfaceInfo(
89+
val surfaceManager: SurfaceManager,
90+
val config: MultiPreviewConfig
91+
)
6392
private var encoderWidth = 0
6493
private var encoderHeight = 0
6594
private var encoderRecordWidth = 0
@@ -207,6 +236,10 @@ class GlStreamInterface(private val context: Context): OnFrameAvailableListener,
207236
surfaceManagerPhoto.release()
208237
surfaceManagerEncoder.release()
209238
surfaceManagerEncoderRecord.release()
239+
multiPreviewSurfaceManagers.values.forEach { info ->
240+
info.surfaceManager.release()
241+
}
242+
multiPreviewSurfaceManagers.clear()
210243
surfaceManager.release()
211244
mainRender.release()
212245
}
@@ -292,6 +325,27 @@ class GlStreamInterface(private val context: Context): OnFrameAvailableListener,
292325
surfaceManagerPreview.swapBuffer()
293326
}
294327
}
328+
// render extra multi-preview surfaces (using independent configuration from PreviewSurfaceInfo)
329+
if (multiPreviewSurfaceManagers.isNotEmpty() && mainRender.isReady() && !limitFps) {
330+
// Only draw filters if default preview is not active (to avoid double drawing)
331+
if (!surfaceManagerPreview.isReady) {
332+
if (surfaceManager.makeCurrent()) {
333+
mainRender.drawFilters(true)
334+
surfaceManager.swapBuffer()
335+
}
336+
}
337+
val previewSnapshot = multiPreviewSurfaceManagers.values.toList()
338+
previewSnapshot.forEach { info ->
339+
if (info.surfaceManager.isReady) {
340+
if (info.surfaceManager.makeCurrent()) {
341+
// Each preview uses its own isPortrait and viewPort configuration
342+
mainRender.drawScreenPreview(info.config.width, info.config.height, info.config.isPortrait, info.config.aspectRatioMode, 0,
343+
info.config.verticalFlip, info.config.horizontalFlip, info.config.viewPort)
344+
info.surfaceManager.swapBuffer()
345+
}
346+
}
347+
}
348+
}
295349
}
296350

297351
override fun onFrameAvailable(surfaceTexture: SurfaceTexture?) {
@@ -353,6 +407,56 @@ class GlStreamInterface(private val context: Context): OnFrameAvailableListener,
353407
surfaceManagerPreview.release()
354408
}
355409

410+
/**
411+
* Add a multi-preview surface
412+
* @param surface the surface to add
413+
* @param config configuration for the preview surface
414+
*/
415+
fun addMultiPreviewSurface(surface: Surface, config: MultiPreviewConfig) {
416+
if (surfaceManager.isReady) {
417+
multiPreviewSurfaceManagers.remove(surface)?.surfaceManager?.release()
418+
419+
val w = if (config.width > 0) config.width else if (previewWidth == 0) encoderWidth else previewWidth
420+
val h = if (config.height > 0) config.height else if (previewHeight == 0) encoderHeight else previewHeight
421+
422+
val surfaceMgr = SurfaceManager()
423+
surfaceMgr.eglSetup(surface, surfaceManager)
424+
val finalConfig = MultiPreviewConfig(w, h, config.horizontalFlip, config.verticalFlip, config.aspectRatioMode, config.isPortrait, config.viewPort)
425+
multiPreviewSurfaceManagers[surface] = PreviewSurfaceInfo(surfaceMgr, finalConfig)
426+
}
427+
}
428+
429+
fun removeMultiPreviewSurface(surface: Surface) {
430+
multiPreviewSurfaceManagers.remove(surface)?.surfaceManager?.release()
431+
}
432+
433+
fun removeAllMultiPreviewSurfaces() {
434+
multiPreviewSurfaceManagers.values.forEach { info ->
435+
info.surfaceManager.release()
436+
}
437+
multiPreviewSurfaceManagers.clear()
438+
}
439+
440+
fun updateMultiPreviewConfig(surface: Surface, config: MultiPreviewConfig): Boolean {
441+
val info = multiPreviewSurfaceManagers[surface] ?: return false
442+
443+
info.config.width = if (config.width > 0) config.width else if (previewWidth == 0) encoderWidth else previewWidth
444+
info.config.height = if (config.height > 0) config.height else if (previewHeight == 0) encoderHeight else previewHeight
445+
info.config.horizontalFlip = config.horizontalFlip
446+
info.config.verticalFlip = config.verticalFlip
447+
info.config.aspectRatioMode = config.aspectRatioMode
448+
info.config.isPortrait = config.isPortrait
449+
info.config.viewPort = config.viewPort
450+
451+
return true
452+
}
453+
454+
fun hasMultiPreviewSurface(surface: Surface): Boolean {
455+
return multiPreviewSurfaceManagers.containsKey(surface)
456+
}
457+
458+
fun getMultiPreviewSurfaceCount(): Int = multiPreviewSurfaceManagers.size
459+
356460
override fun setStreamRotation(orientation: Int) {
357461
this.streamOrientation = orientation
358462
}

0 commit comments

Comments
 (0)