Repository navigation
feat: per-slot Mirror in the camera settings (#226) - #246
Conversation
Mirror flips the picture left to right, after the slot's Turn, in the single view and the grid. A SurfaceView cannot be mirrored with setScaleX(-1) and KEY_ROTATION has no mirror, so a mirrored H.264/H.265 slot renders through GlMirror: the decoder outputs into a SurfaceTexture and an OpenGL ES 2 renderer on its own thread draws each frame turned and flipped onto the view's surface. Unmirrored slots keep the decoder drawing straight onto the SurfaceView (#76). The GL path is released after the decoder, so the view's surface is free for the next one; if EGL fails, the picture is shown unmirrored. MJPEG is flipped while it is drawn in software (the hardware JPEG decoder cannot mirror). The single view's layout now asks the decoder whether it swaps the picture's sides, which covers the GL path's turn on any Android version. /stats.json gets mirror: true for mirrored slots and cells.
#226) Saving the settings restarts the single view. The old decode thread was still busy with a frame, created a decoder with the old turn and mirror, and won the race against the new thread; the new one's attempt failed on the busy surface and still left its turn in appliedRotation, so the picture had the new layout but the old orientation. Now the old thread stops before creating a decoder, closeDecoder() discards a creation that is still running, and appliedRotation is set only by the decoder that stays.
3050446 to
30402e8
Compare
|
Device test, Aceline-1 (Android 10, Allwinner H616), CI build
Not from this PR: in |
PR Summary by QodoAdd per-slot camera mirroring for single and grid views
AI Description
Diagram
High-Level Assessment
Files changed (16)
|
Code Review by Qodo
1.
|
… stats (#226) - A decoder made by a video thread whose stream was restarted with new settings is dropped when installed, not only before creation. - The GL path is released even when releasing a failed codec throws. - A queued frame is no longer drawn once the GL path is being released. - /stats.json: mirror_failed: true when Mirror is on but the GL path failed and the decoder draws unmirrored; unused framesDrawn removed.
…226) Android counts eglInitialize per display, so eglTerminate only drops this instance's reference; other mirrored cells keep the display.
|
/review |
|
Code review by qodo was updated up to the latest commit 4d046c3 |
…ror_failed (#226) eglQuerySurface and eglSwapBuffers results are checked; the first failure after setup is logged with the EGL error, and /stats.json reports mirror_failed for that decoder.
|
/review |
|
Code review by qodo was updated up to the latest commit aed8dcb |
Mirror (#246) merged first: its GL path is released on the new failure and replace paths (failDecoder, dropDecoder); version 2.18.1, versionCode 44.
Closes #226.
Mirror in the camera settings (a checkbox under Turn): flips the picture left to right, after the turn, in the single view and the grid, for H.264, H.265 and MJPEG.
Why a separate drawing path. The hardware decoder draws straight onto a
SurfaceView;setScaleX(-1)on it is ignored (the system composes the surface) andMediaFormat.KEY_ROTATIONhas no mirror (#201, #225).Change.
GlMirror: for a mirrored H.264/H.265 slot the decoder renders into aSurfaceTexture; an OpenGL ES 2 renderer on its own thread draws each frame onto the view's surface with one transform (turn counter-clockwise, then flip on the screen). It also does the slot's turn, soKEY_ROTATIONis not set on this path. Unmirrored slots keep the decoder drawing straight onto theSurfaceView(ANR on AG-216: RenderThread blocked in eglSwapBuffers with 8 multi-view SurfaceViews #76).MediaCodecManager: the GL path belongs to the decoder and is released after it on every release path (close, pause, codec error, lost race), so the view's surface is free for the next decoder. If EGL fails, the picture is shown unmirrored (logged).MJPEG: flipped while drawn in software; the hardware JPEG decoder cannot mirror, so a mirrored MJPEG slot is decoded in software.
The single view's layout asks the decoder whether it swaps the sides (
swapsSides()), which also covers the GL path's turn on Android 5.Settings:
mirror_<slot>per slot;/stats.json:mirror: trueinsingleand in cells. README (three languages),docs/usage.md,docs/debug.md.Local Docker build as CI:
testDebugUnitTest(incl.GlMirrorTest: the 8 orientations),lintDebug,assembleDebugAceline-1 (CI build 30402e8): single and grid, H.264 / H.265 / MJPEG, Mirror × Turn, snapshots and screenshots, fps in the 3×3 grid