# SGR Vision (Android) — LLM / Coding-Agent Guide > 이 문서는 코딩 에이전트가 Android 네이티브 SDK 를 정확히 사용하도록 돕는 압축 레퍼런스입니다. > 사람용 소개는 vision-sdk-android/README.md, 실행 예제는 example/android 를 참고하세요. > 웹 SDK 가이드는 llms.txt, iOS 는 ios-llms.txt. 세 SDK 는 동일한 옵션·프로필·라이선스 계약을 공유합니다. ## What it is OpenGL ES 기반 Android 네이티브 SDK. **Media3(ExoPlayer)** 가 재생하는 영상 프레임을 Media3 `Effect` 파이프라인(GLSL ES 패스 체인)으로 실시간 화질 개선해 출력한다. **재생은 사용자의 `ExoPlayer` 가 담당하고, SDK 는 그 프레임의 화면 출력만 개선한다.** 웹 SDK 의 `enhance()` / iOS `VisionEnhancer` 에 대응하는 네이티브 포트. 핵심 API 2개: - `VisionEnhancer` — 명령형 컨트롤러(권장). `ExoPlayer` 에 GL 이펙트를 붙인다. 라이선스 게이팅 내장. - `VisionPlayerView` — Media3 `PlayerView` 를 감싼 `FrameLayout` 호스트. Compose 는 `AndroidView` 로 감싼다. ## Install (raw GitHub Maven repo — 토큰 불필요) ```kotlin // settings.gradle.kts dependencyResolutionManagement { repositories { google(); mavenCentral() maven { url = uri("https://raw.githubusercontent.com/SGRsoft-Dev/vision-android-sdk/main") } } } // app/build.gradle.kts implementation("com.sgrsoft.vision:vision:0.1.9") ``` - 배포: 컴파일된 `aar`(raw maven 레이아웃). **minSdk 24 · compileSdk 34 · JDK 17 · Kotlin**. - Media3(`media3-common`/`exoplayer`/`effect`/`ui` `1.4.1`)는 `api` 의존성으로 전이 포함. - 리소스(profiles.json + GLSL ES 셰이더)는 aar 에 내장. - **중요:** 공개 API 가 Media3 `@UnstableApi` 표면을 노출 → 호출부에 `@OptIn(UnstableApi::class)` 필요. ## Primary API — VisionEnhancer (권장) ```kotlin import androidx.media3.common.MediaItem import androidx.media3.common.util.UnstableApi import androidx.media3.exoplayer.ExoPlayer import androidx.media3.ui.PlayerView import com.sgrsoft.vision.VisionEnhancer import com.sgrsoft.vision.VisionOptions import com.sgrsoft.vision.VisionProfile @OptIn(UnstableApi::class) fun attach(context: Context, url: String): Pair { val player = ExoPlayer.Builder(context).build().apply { setMediaItem(MediaItem.fromUri(url)); prepare(); playWhenReady = true // 재생은 사용자가 제어 } val view = PlayerView(context).apply { this.player = player } val enhancer = VisionEnhancer( player = player, options = VisionOptions(profile = VisionProfile.default), context = context, // packageName 으로 라이선스 검증 onLicense = { state -> /* pending → ok / invalid */ }, ) // enhancer.update(...) / enhancer.setEnabled(false) / enhancer.getState() / enhancer.destroy() return player to view } ``` - `VisionEnhancer(player, options, context?, deviceInfo?, onLicense?)`. `licenseKey` 지정 시 검증(app id 는 OS 에서 도출, `context` 는 packageName 폴백). - 옵션은 라이브 갱신: `enhancer.update(newOptions)` — 이펙트 재생성 없이 공유 state 로 반영(뷰 재구성 불필요). - 소스 전환: 같은 `player` 에 `setMediaItem(...).prepare()` — 이펙트가 그대로 유지됨. ## View host API — VisionPlayerView ```kotlin val v = VisionPlayerView(context) // FrameLayout(내부에 PlayerView) v.setPlayer(player, VisionOptions(profile = VisionProfile.ott), onLicense = { s -> }) v.update(VisionOptions(profile = VisionProfile.oled)) v.release() // Compose: AndroidView(factory = { VisionPlayerView(it).apply { setPlayer(player, options) } }) ``` ## VisionOptions (전체 — 웹 VideoEnhancementOptions / iOS VisionOptions 미러, 동일 필드명) ```kotlin VisionOptions( enabled: Boolean = true, profile: VisionProfile? = VisionProfile.default, // off/default/detail/ott/education/cctv/anime/oled/retro // 개별 강도 — 명시 시 profile 기본값을 덮어씀 sharpen: Double? = null, // 0..1 언샤프 선명도 contrast: Double? = null, // 0..1 글로벌 S-curve cleanup: Double? = null, // 0..1 압축 노이즈/블록 제거 anime: Double? = null, // 0..1 라인 강조 + 색면 평탄화 gamma: Double? = null, // 0.5..2.0 (기본 1.0) localContrast: Double? = null, // 0..1 와이드 radius 언샤프 darkLift: Double? = null, // 0..1 암부만 lift scanline: ScanlineParams? = null, // enabled/intensity/spacing upscale: UpscaleParams? = null, // 아래 edgeEnhancement: EdgeEnhancementParams? = null, // enabled/algorithm(EdgeAlgorithm.off/classic/rcas)/strength cnn1: Cnn1Params? = null, // 1-layer CNN(ES3.1 compute) cnn: CnnParams? = null, // multi-layer CNN(layers:List 81 floats/layer) renderer: VisionRenderer? = null, // auto/gles/vulkan (현재 gles 단일 백엔드) autoReduceOnLowEnd: Boolean? = null, // 저사양 기기 강도 자동 감쇠(기본 true) licenseKey: String? = null, // 지정 시 효과 즉시 적용→2초 후 검증, 실패 시 효과 OFF(원본) ) // UpscaleParams(enabled:Boolean?, algorithm:UpscaleAlgorithm?, target:UpscaleTarget?, width:Int?, height:Int?, sharpness:Double?) // UpscaleAlgorithm: none/nearest/bilinear/lanczos/fsr (web alias 정규화: lanczos2→lanczos, easu→fsr) // UpscaleTarget: source/p720("720p")/p1080("1080p")/p1440("1440p")/x2/x3/custom ``` ## Profiles (한 줄 의미) - default: Cinematic, 실사용 메인 / detail: Ultra Detail, 시연용 - ott: 저비트레이트 스트리밍 cleanup / education: 텍스트·판서 가독성 - cctv: 야간·저조도 대비 / anime: 라인+색면 평탄화 - oled: 깊은 black+영화적 대비 / retro: CRT scanline+warm / off: 비활성 ## Upscale (UpscaleParams 로 직접 구성 — 권장 조합 예시) ```kotlin // 비활성 → upscale 생략(null) val stable = UpscaleParams(enabled = true, algorithm = UpscaleAlgorithm.lanczos, target = UpscaleTarget.p1080, sharpness = 0.04) val quality = UpscaleParams(enabled = true, algorithm = UpscaleAlgorithm.fsr, target = UpscaleTarget.p1080, sharpness = 0.06) val strong = UpscaleParams(enabled = true, algorithm = UpscaleAlgorithm.fsr, target = UpscaleTarget.p1080, sharpness = 0.10) val lowPower = UpscaleParams(enabled = true, algorithm = UpscaleAlgorithm.bilinear, target = UpscaleTarget.p720, sharpness = 0.03) ``` ## License (네이티브 = packageName 바인딩, 낙관적 게이팅) - **낙관적 라이프사이클**: `licenseKey` 지정 시 효과가 **즉시 적용**되고, **2초 후** 검증한다. 통과면 그대로 유지, 실패(없음/만료/폐기/packageName불일치/네트워크)면 효과를 끄고 **원본으로 되돌림**. 결과는 프로세스 내 캐시(재요청 X). 웹/iOS 와 동일한 동작. - 상태: `LicenseState` = `none|pending|ok|invalid`. - `VisionEnhancer(onLicense = { state -> ... })` 또는 `enhancer.getState().license` / `enhancer.licenseState` - **웹과 차이**: 네이티브는 도메인(Origin) 대신 앱 **packageName** 을 `app` 으로 서버에 전송한다. 라이선스의 `allowedOrigins` 에 앱의 **packageName** 이 포함돼야 통과(웹 도메인과 같은 리스트에 함께). - **개발 빌드 면제**(웹 localhost 대응): 디버그 빌드(`ApplicationInfo.FLAG_DEBUGGABLE`)는 검증을 건너뛰고 바로 구동된다(licenseState=ok). **릴리스 APK 만 검증**(Play 가 debuggable 릴리스 거부 → 출시 빌드는 면제 신호를 위조 불가). - **packageName 위변조 방지**: app 식별자는 SDK(LicenseClient)가 프레임워크 `ActivityThread.currentPackageName()`(시스템이 프로세스 bind 시 설정)에서 **직접** 읽는다. 호출자가 넘긴 `context.packageName` 은 폴백일 뿐이라 `ContextWrapper` 등으로 다른 앱 ID 위조 불가. - 본인 앱은 packageName 을 origins 에 등록한 키를 support@sgrsoft.com 으로 발급. - `INTERNET` 권한 필요(라이브러리 매니페스트에 이미 선언됨). 검증 서버 주소는 SDK 에 고정 내장(변경 불가). ## Namespace / Exports (public) - 뷰/컨트롤러: `VisionEnhancer`, `VisionPlayerView`, `VisionGlEffect`(Media3 GlEffect) - 옵션/타입: `VisionOptions`, `VisionProfile`, `VisionRenderer`, `UpscaleAlgorithm`, `UpscaleTarget`, `EdgeAlgorithm`, `ScanlineParams`, `UpscaleParams`, `EdgeEnhancementParams`, `Cnn1Params`, `CnnParams`, `CnnLayer`, `CnnWeights`, `EnhancementUniforms`, `VisionState`(`drmFallback` 포함), `LicenseState` - 네임스페이스: `VisionSdk.version`, `VisionSdk.specVersion`, `VisionSdk.profiles` - `VisionEnhancer.setCompareSplit(0.5f)` — 좌(CNN 적용)/우(미적용) 좌우 분할 비교 렌더(데모 '동시 비교'). `<0`(예: -1f)이면 끔. - Multi-layer CNN 가중치: `CnnWeights.parse(bytes, layerCount)` 로 .bin(LE f32, 81 floats/layer)을 `List` 로 만들어 `VisionOptions(cnn = CnnParams(enabled = true, layers = …))` 에 주입(URL 은 직접 fetch). ## Behavior & constraints (에이전트 주의) - 진입점은 `VisionEnhancer`(명령형, 권장) 또는 `VisionPlayerView`(뷰 호스트). 둘 다 `@OptIn(UnstableApi::class)`. - `enabled=false` 또는 `profile=off` 또는 모든 강도 0 이면 passthrough(원본) 출력. - 낙관적 게이팅: 효과가 먼저 적용되고 2초 후 검증 → 실패 시에만 원본으로 되돌린다(뷰 재구성 불필요). - 백엔드는 OpenGL ES. CNN 패스는 **GL 컨텍스트가 ES 3.1 compute 를 지원할 때만** 동작하고, 미지원이면 자동 생략한다(웹 WebGL 폴백과 동일 — 크래시 없음). 나머지 패스(combined/upscale/RCAS)는 ES2+ 동작. - DRM(Widevine 등) 보호 콘텐츠는 secure surface 라 GL 처리 불가 → 개선 적용 불가. 단, SDK 가 암호화 비디오 트랙(`Format.drmInitData`)을 감지하면 이펙트를 떼어 **검정 대신 원본을 그대로 재생**한다 (`onTracksChanged` 기준 자동 attach/detach, `VisionState.drmFallback`). 클리어 소스로 바뀌면 자동 재부착. - 비율·레이아웃·전체화면은 호출측 책임. SDK 는 Media3 이펙트로 프레임만 처리한다. ## Common agent tasks - "영상에 화질 개선" → ExoPlayer 만들고 `VisionEnhancer(player, VisionOptions(profile = VisionProfile.default), context)` - "라이선스 적용" → `VisionEnhancer(player, VisionOptions(profile = VisionProfile.default, licenseKey = "VSK-…"), context, onLicense = { s -> })` (체험 키는 발급된 packageName 에만 바인딩 — 본인 앱은 packageName 등록 키 발급) - "끄기/켜기" → `enhancer.setEnabled(bool)` - "프로필 변경" → `enhancer.update(options.copy(profile = VisionProfile.ott))` - "업스케일" → options.upscale 에 위 preset 중 하나(quality 권장: 1080p+FSR) - "소스 전환" → 같은 player 에 `player.setMediaItem(MediaItem.fromUri(url)); player.prepare()` (이펙트 유지) - "Compose 에서" → `AndroidView(factory = { PlayerView(it).apply { player = exo } })` + `VisionEnhancer(exo, options, ctx)` - "정리" → `enhancer.destroy()` 후 `player.release()` (VisionPlayerView 는 `release()`) ## Links - 배포(raw Maven): https://github.com/SGRsoft-Dev/vision-android-sdk (com.sgrsoft.vision:vision:0.1.9) - 예제 앱: example/android (Compose 데모 — 소스/상태/효과/프리셋/전체화면/다크모드) - 웹 SDK 가이드: llms.txt · iOS: ios-llms.txt