μ΄ μ ν리μΌμ΄μ μ μ€μκ° μΉ΄λ©λΌ μ€νΈλ¦¬λ°μ μ§μ° μΆλ ₯ κΈ°λ₯μ μΆκ°ν Electron κΈ°λ° λ°μ€ν¬ν± μ ν리μΌμ΄μ μ λλ€. μ¬μ©μκ° μ€μ ν μκ°λ§νΌ μ§μ°λ μμμ λ³Ό μ μμΌλ©°, λ Ήν λ° μ¬μ κΈ°λ₯μ μ 곡ν©λλ€.
Dev. JIRA URL : http://jira.lge.com/issue/browse/SICDTV-15711
git clone ssh://[email protected]:2222/media_bsp/apps/camera.gitcd existing_repo
git remote add origin http://source.lge.com/gitlab/media_bsp/apps/camera.git
git branch -M main
git push -uf origin main- HTML5 Canvas: λΉλμ€ νλ μ λ λλ§
- Vanilla JavaScript (ES6+): λͺ¨λ μμ€ν μ¬μ©
- Tailwind CSS: User Interface / User Experience μ€νμΌλ§
- Node.js: v18.0.0+
- Electron: v36.2.1 - ν¬λ‘μ€ νλ«νΌ λ°μ€ν¬ν± μ± νλ μμν¬
- Chokidar: v4.0.3 - νμΌ μμ€ν κ°μ
- V4L2 API: Linux λΉλμ€ μΊ‘μ²
- C: μ μμ€ μΉ΄λ©λΌ μ μ΄
- Linux (V4L2 μΉ΄λ©λΌ μ§μ)
- Windows (μΉ΄λ©λΌ μΊ‘μ² κΈ°λ₯ μ ν)
- Node.js v18.0.0+
- npm v8.0.0+
- Linux: gcc, make (λ€μ΄ν°λΈ λͺ¨λ λΉλμ©)
- O24 or O26 보λμμ μ 체 νλ©΄μ μ§μνκΈ° μν΄μλ λ€μ λ€μ patch λ₯Ό λ°μνμ¬ bulssi λΉλ
git clone ssh://[email protected]:2222/media_bsp/apps/camera.gitcd camera
npm install보λμμμλ bulssi_install.sh λ₯Ό μννμ¬ μΆκ° ν¨ν€μ§ μλ μ€μΉ
npm run build보λμμμλ λΆν ν bulssi_init.sh λ¨Όμ μν
npm startnpm run start:wingraph LR
subgraph "Native Layer"
V4L2[V4L2 Camera API]
CAP[capture_interface.h]
VCAP[v4l2_capture.c]
end
subgraph "Backend Process"
MAIN[electron/main.js<br/>Main Process]
FH[FrameHandler<br/>νλ μ μ²λ¦¬]
CAPTURE[capture.js<br/>μΉ΄λ©λΌ μ μ΄]
FW[frame-watcher.js<br/>νμΌ κ°μ]
PRELOAD[preload.js<br/>IPC Bridge]
end
subgraph "Frontend (Renderer)"
HTML[index.html]
APP[app-init.js]
VIEWER[mjpeg-viewer.js<br/>λ©μΈ 컨νΈλ‘€λ¬]
FM[frame-manager.js<br/>νλ μ κ΄λ¦¬]
UI[ui-controller.js<br/>UI μ μ΄]
CONFIG[config.js<br/>μ€μ ]
UTILS[utils.js<br/>μ νΈλ¦¬ν°]
end
subgraph "File System"
LIVE[(live/)]
RECORD[(record/)]
end
V4L2 --> VCAP
VCAP --> CAP
CAP --> CAPTURE
CAPTURE --> FH
FH --> FW
FW --> LIVE
FH --> RECORD
MAIN --> FH
MAIN --> PRELOAD
PRELOAD -.IPC.-> VIEWER
FW -.νμΌ λ³κ²½ μλ¦Ό.-> MAIN
MAIN -.νλ μ λ°μ΄ν°.-> VIEWER
HTML --> APP
APP --> VIEWER
APP --> FULLSCREEN[fullscreen-manager.js<br/>μ 체νλ©΄ κ΄λ¦¬]
VIEWER --> FM
VIEWER --> UI
VIEWER --> CONFIG
VIEWER --> UTILS
FM --> UTILS
UI --> UTILS
camera/
βββ π electron/
β βββ main.js # Electron λ©μΈ νλ‘μΈμ€
βββ π frontend/
β βββ π public/
β β βββ index.html # λ©μΈ UI
β β βββ π styles/ # CSS μ€νμΌ
β β β βββ main.css
β β βββ π resources/ # UI μμ΄μ½, wardrobe
β β βββ π live/ # λΌμ΄λΈ νλ μ μμ μ μ₯
β β βββ π record/ # λ
Ήν νλ μ μ μ₯
β βββ π src/
β βββ app-init.js # μ± μ΄κΈ°ν
β βββ mjpeg-viewer.js # λ©μΈ 컨νΈλ‘€λ¬
β βββ frame-manager.js # νλ μ κ΄λ¦¬
β βββ ui-controller.js # UI μ μ΄
β βββ fullscreen-manager.js # μ 체νλ©΄ λͺ¨λ κ΄λ¦¬
β βββ config.js # μ€μ μμ
β βββ utils.js # μ νΈλ¦¬ν°
β βββ dom.js # $, on, show, wait λ± DOM μ νΈ
β βββ number-input.js # Delay/Speed μ€νΌλ λ°μΈλ©
β βββ capture-helper.js # νμ¬ νλ μ μΊ‘μ²(webcam/mjpeg)
β βββ vton-ui.js # λ‘λ©/νλ‘κ·Έλ μ€/μλ¬ UI
β βββ vton-service.js # μ€μ API + ν΄λ°±(Mock) ν΅ν©
β βββ wardrobe-controller.js # μλλ‘λΈ ν΄λ¦ β VTON μ€ν
βββ π backend/
β βββ π src/
β βββ capture.js # μΉ΄λ©λΌ μΊ‘μ² μ μ΄
β βββ frame-watcher.js # νμΌ μμ€ν
κ°μ
β βββ preload.js # Electron IPC
β βββ server.js # μλ² (νμ₯μ©)
β βββ π routes/
β βββ vton.js # /api/v1/vton/jobs
βββ π native/
β βββ π linux/
β βββ capture_interface.h
β βββ v4l2_capture.c # V4L2 μΉ΄λ©λΌ ꡬν
β βββ Makefile
βββ package.json
μ ν리μΌμ΄μ μ 4κ°μ§ μ£Όμ μνλ₯Ό κ°μ§λλ€:
stateDiagram-v2
[*] --> IDLE
IDLE --> LIVE: Live λ²νΌ
LIVE --> RECORD: Record λ²νΌ
LIVE --> IDLE: Live λ²νΌ
RECORD --> PLAYBACK: Record λ²νΌ
PLAYBACK --> IDLE: Playback λ²νΌ
IDLE --> PLAYBACK: Play/Playback λ²νΌ
state IDLE {
[*] --> λκΈ°μ€
λκΈ°μ€: μ΄κΈ° μν
}
state LIVE {
[*] --> μ€νΈλ¦¬λ°
μ€νΈλ¦¬λ°: μ€μκ° μΉ΄λ©λΌ μΆλ ₯
μ€νΈλ¦¬λ°: μ§μ° μκ° μ μ© κ°λ₯
}
state RECORD {
[*] --> λ
Ήνμ€
λ
Ήνμ€: μ€νΈλ¦¬λ° + νμΌ μ μ₯
}
state PLAYBACK {
[*] --> μ¬μ
μ¬μ: λ
Ήνλ μμ μ¬μ
μ¬μ --> μΌμμ μ§
μΌμμ μ§ --> μ¬μ
}
- μ΄κΈ° μν
- Live λλ Playback λͺ¨λ μ§μ κ°λ₯
- μ€μκ° μΉ΄λ©λΌ μ€νΈλ¦¬λ°
- μ§μ° μΆλ ₯: 0~10μ΄ μ€μ κ°λ₯
- μ€μκ° μ§μ° λ³κ²½: μ€νΈλ¦¬λ° μ€λ¨ μμ΄ Delay κ° μ‘°μ κ°λ₯
- νλ μμ
frontend/public/live/λλ ν 리μ μμ μ μ₯
- Live λͺ¨λμμ 무μ€λ¨ μ ν
- μ€νΈλ¦¬λ° κ³μνλ©΄μ νλ μμ
frontend/public/record/μ μ μ₯ - λ
Ήν μ 보λ₯Ό
rec_info.jsonμ κΈ°λ‘
- λ Ήνλ νλ μ μνμ€ μ¬μ
- λ€μν μ¬μ μ μ΄:
- μ λ°©ν₯/μλ°©ν₯ μ¬μ
- μλ μ‘°μ (0.2x ~ 4.0x)
- νλ μ λ¨μ μ΄λ
- μ²μ/λμΌλ‘ μ΄λ
- λ°λ³΅ μ¬μ
- μ€μκ° μΉ΄λ©λΌ μ€νΈλ¦¬λ°
- μ§μ° μΆλ ₯ κΈ°λ₯: 0~10μ΄ μ¬μ΄ μ€μ
- μ€μκ° Delay λ³κ²½: μ€νΈλ¦¬λ° μ€μλ μ§μ° μκ° μ‘°μ κ°λ₯
- Linuxμμ V4L2 μΉ΄λ©λΌ μλ κ°μ§
- Live λͺ¨λμμ μ¦μ λ Ήν μμ
- μ€νΈλ¦¬λ° μ€λ¨ μμ΄ λ¬΄μ€λ¨ λ Ήν
- JPEG μνμ€λ‘ μ μ₯
- λ Ήν λ©νλ°μ΄ν° μλ μ μ₯
- λ Ήνλ μμ μ¬μ
- μ¬μ μ μ΄:
- Play/Pause (μ¬μ/μΌμμ μ§)
- Reverse (μμ¬μ)
- Next/Previous Frame (νλ μ μ΄λ)
- Rewind/Fast Forward (μ²μ/λ)
- μ¬μ μ΅μ
:
- Speed: 0.2x ~ 4.0x (0.2 λ¨μ)
- Repeat: λ°λ³΅ μ¬μ
- Flip: μ’μ° λ°μ
- νλ‘κ·Έλ μ€ λ°λ‘ νΉμ μμΉ μ΄λ
- F11 ν€ λλ Full λ²νΌμΌλ‘ μ 체νλ©΄ μ ν
- μ 체νλ©΄μμ Escape ν€λ‘ μ’ λ£
- μλ 컨νΈλ‘€ μ¨κΉ: λ§μ°μ€ μ μ§ μ μλμΌλ‘ 컨νΈλ‘€ μμ μ¨κΉ
- λ§μ°μ€ νΈλ² κ°μ§: νλ©΄ νλ¨ 100px μ΄λ΄λ‘ λ§μ°μ€ μ΄λ μ 컨νΈλ‘€ νμ
- λͺ¨λ λͺ¨λ(Live, Record, Playback)μμ μ¬μ© κ°λ₯
- Live λͺ¨λ μ€ μ§μ° μκ° λ³κ²½ κ°λ₯
- Delay κ° λ³κ²½ μ μλμΌλ‘ μ€νΈλ¦¬λ° μ¬μμ
- 0~10μ΄ λ²μμμ μ€μκ° μ‘°μ
- μΉ΄λ©λΌ μΊ‘μ² νλ‘μΈμ€ κ΄λ¦¬
- Live/Record λͺ¨λ μ ν μ μ΄
- νλ μ νμΌ κ΄λ¦¬ λ° μ 리
- V4L2 μΉ΄λ©λΌμ ν΅μ
- Unix Socket κΈ°λ° IPC
- μΉ΄λ©λΌ μ€μ λ° μ μ΄
- Chokidar κΈ°λ° νμΌ κ°μ
- μ νλ μ κ°μ§ λ° μ λ¬
- μλ μ¬μμ λ©μ»€λμ¦ (μ΅λ 3ν)
- λ©μΈ μ ν리μΌμ΄μ 컨νΈλ‘€λ¬
- μν κ΄λ¦¬ λ° μ ν λ‘μ§
- μ¬μ©μ μ λ ₯ μ²λ¦¬
- νλ μ λ°μ΄ν° κ΄λ¦¬
- μ΄λ―Έμ§ λ‘λ© λ° μΊμ±
- νλ μ νμ λ° μΈλ±μ±
- DOM μμ μ μ΄
- Canvas λ λλ§
- λ²νΌ μν μ λ°μ΄νΈ
- μ λλ©μ΄μ ν¨κ³Ό
- μ 체νλ©΄ λͺ¨λ μ ν κ΄λ¦¬
- F11 ν€ λ° λ²νΌ μ΄λ²€νΈ μ²λ¦¬
- μλ 컨νΈλ‘€ μ¨κΉ/νμ
- λ§μ°μ€ μμ§μ κ°μ§
// electron/main.js - FrameHandler.startCapture()
const device = new capture.Device({
width: 640, // ν΄μλ λλΉ
height: 360, // ν΄μλ λμ΄
fps: 24, // μ΄λΉ νλ μ
numFiles: 28 // λ²νΌ νμΌ μ (fps * delay + 4)
});μ΄ νλ‘μ νΈλ LG Electronics Inc. CTO SoC Center λ΄λΆ νλ‘μ νΈλ‘ μμ§ λ°°ν¬λμ§ μμ΅λλ€.
κ°λ°ν: LG Electronics SoC Media BSP Task
μ΄λ©μΌ: [email protected]
