Add live preview, WiFi access code, and OBS-free virtual camera

- Live camera preview on phone and PC
- WiFi access code (PIN): USB/loopback exempt, blocks strangers on the network
- Bundled Unity Capture (MIT) virtual camera driver - OBS now optional
- Larger default window, sidebar overflow protection
- Full install/build instructions in all 10 language READMEs
This commit is contained in:
This-null
2026-08-24 19:04:09 +03:00
parent 3fd19859c0
commit 9190fd99e1
33 changed files with 881 additions and 210 deletions

View File

@@ -25,37 +25,44 @@
---
> [!CAUTION]
> **FlexCam requires [OBS Studio](https://obsproject.com/download) to be installed.**
> It uses the OBS Virtual Camera driver to expose your phone as a webcam.
> Install OBS once — you don't need to keep it open.
---
## What it is
FlexCam streams your Android phone's camera to your PC and exposes it as
**"OBS Virtual Camera"**, so any app (Zoom, Teams, Discord, your browser, OBS)
can pick it as a webcam. It works over **USB** (via ADB) and **WiFi**, and
switches between them automatically if one drops.
FlexCam streams your Android phone's camera to your PC and exposes it as a
**virtual webcam**, so any app (Zoom, Teams, Discord, your browser, OBS) can
pick it as a camera. It works over **USB** (via ADB) and **WiFi**, and switches
between them automatically if one drops.
- Hybrid **USB + WiFi** with automatic failover
- **Auto-connects** when your phone is ready — no manual steps
- **Front / back** camera switch
- **Auto portrait/landscape** (follows the phone's rotation)
- **Live preview** on both phone and PC
- **Front / back** camera switch, **auto portrait/landscape**
- Keeps streaming with the **screen off**
- **WiFi access code** — strangers on your network can't view the stream
- **10 languages**, dark UI, tray support
- Runs fully on your device — **no data leaves your computer**
- Runs fully on your device — **no data ever leaves your computer**
## Virtual camera driver
FlexCam needs a virtual camera on the PC. You have two options:
- **Bundled driver (no OBS needed):** on first run, if no virtual camera is
found, click **"Install virtual camera"** in the app. It registers a small
open-source driver ([Unity Capture](https://github.com/schellingb/UnityCapture),
MIT) once, with a one-time admin prompt.
- **OBS Studio:** if you already have [OBS](https://obsproject.com/download),
FlexCam uses its Virtual Camera automatically.
## Quick start
1. Install **OBS Studio** (once).
2. On the phone: install `release/FlexCam.apk` and open the app.
3. On the PC: run **FlexCam** and it auto-connects.
4. In your video app, choose the camera **"OBS Virtual Camera"**.
1. **Phone:** install `FlexCam.apk` and open the app.
2. **PC:** unzip **FlexCam-Windows.zip** and run `FlexCam.exe`.
- First run: if prompted, click **"Install virtual camera"** (one-time).
3. It **auto-connects** over USB. For WiFi, type the phone's IP and the
**access code** shown in the app.
4. In your video app, pick the camera **"OBS Virtual Camera"** (or
**"Unity Video Capture"** if you used the bundled driver).
USB needs **USB debugging** enabled (Developer Options). WiFi needs no cable
just type the IP the phone app shows.
> USB needs **USB debugging** enabled (Developer Options). WiFi needs no cable.
## Build from source
@@ -74,10 +81,19 @@ python -m venv .venv
.venv\Scripts\python webgui.py
```
Package a standalone `.exe` with PyInstaller (onedir):
```
.venv\Scripts\python -m PyInstaller --noconfirm --onedir --windowed --name FlexCam ^
--add-data "web;web" --add-data "flexcam-logo.png;." --add-data "FlexCam.apk;." ^
--add-data "adb;adb" --add-data "virtualcam;virtualcam" ^
--collect-all pyvirtualcam --collect-all pystray --collect-all webview ^
--collect-all pythonnet --collect-all clr_loader --collect-all pypresence webgui.py
```
## Tech
- **Android:** Kotlin, CameraX, MJPEG server on port `8474`.
- **PC:** Python, pywebview UI, `pyvirtualcam` → OBS Virtual Camera.
- **Android:** Kotlin, CameraX, an MJPEG server on port `8474`.
- **PC:** Python, pywebview UI, `pyvirtualcam` → OBS or Unity Capture.
- **Transport:** raw TCP over `adb forward` (USB) or the phone's IP (WiFi).
## License

View File

@@ -38,6 +38,7 @@ class CameraCapture(
fun pause() {
paused = true
latest.set(null)
FrameBus.jpeg = null
ContextCompat.getMainExecutor(context).execute { provider?.unbindAll() }
}
@@ -59,7 +60,9 @@ class CameraCapture(
.setTargetRotation(Surface.ROTATION_0)
.build().also { a ->
a.setAnalyzer(executor) { image ->
latest.set(yuvToJpeg(image))
val j = yuvToJpeg(image)
latest.set(j)
FrameBus.jpeg = j
image.close()
}
}

View File

@@ -4,12 +4,15 @@ import android.Manifest
import android.content.Intent
import android.content.pm.PackageManager
import android.content.res.ColorStateList
import android.graphics.BitmapFactory
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.view.View
import android.widget.Button
import android.widget.ImageView
import android.widget.TextView
import kotlin.concurrent.thread
import androidx.core.content.ContextCompat.getColor
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AlertDialog
@@ -58,11 +61,13 @@ class MainActivity : AppCompatActivity() {
}
val ip = wifiIp()
findViewById<TextView>(R.id.hintText).text = if (ip != null) {
val base = if (ip != null) {
getString(R.string.hint_with_ip, ip, Config.PORT)
} else {
getString(R.string.hint_no_ip, Config.PORT)
}
findViewById<TextView>(R.id.hintText).text =
base + "\n" + getString(R.string.access_code, Pin.get(this))
findViewById<Button>(R.id.btnGithub).setOnClickListener { open(Config.GITHUB_URL) }
findViewById<Button>(R.id.btnDiscord).setOnClickListener { open(Config.DISCORD_URL) }
@@ -113,6 +118,34 @@ class MainActivity : AppCompatActivity() {
if (hasFocus && !userStopped) maybeStartService()
}
@Volatile private var previewing = false
override fun onResume() {
super.onResume()
if (previewing) return
previewing = true
val view = findViewById<ImageView>(R.id.preview)
thread(name = "preview") {
while (previewing) {
val j = FrameBus.jpeg
if (j != null) {
val bmp = try {
BitmapFactory.decodeByteArray(j, 0, j.size)
} catch (_: Exception) {
null
}
if (bmp != null) runOnUiThread { view.setImageBitmap(bmp) }
}
Thread.sleep(66)
}
}
}
override fun onPause() {
super.onPause()
previewing = false
}
private fun requestPermissions() {
val needed = mutableListOf(Manifest.permission.CAMERA)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {

View File

@@ -9,6 +9,7 @@ class MjpegServer(
private val port: Int,
private val frameSource: () -> ByteArray?,
private val control: (String) -> Unit = {},
private val authKey: String = "",
) {
@Volatile private var running = false
private var server: ServerSocket? = null
@@ -46,7 +47,24 @@ class MjpegServer(
try {
client.use {
val requestLine = it.getInputStream().bufferedReader().readLine() ?: ""
val path = requestLine.split(" ").getOrNull(1) ?: "/"
val target = requestLine.split(" ").getOrNull(1) ?: "/"
val path = target.substringBefore("?")
val query = target.substringAfter("?", "")
val loopback = it.inetAddress?.isLoopbackAddress ?: false
if (!loopback && authKey.isNotEmpty()) {
val key = query.split("&")
.firstOrNull { q -> q.startsWith("key=") }
?.substringAfter("key=") ?: ""
if (key != authKey) {
it.getOutputStream().write(
"HTTP/1.0 403 Forbidden\r\nContent-Length: 9\r\n\r\nForbidden".toByteArray()
)
it.getOutputStream().flush()
return
}
}
if (path.startsWith("/stop") || path.startsWith("/start")) {
control(if (path.startsWith("/stop")) "stop" else "start")
val ok = "HTTP/1.0 200 OK\r\nContent-Length: 2\r\n\r\nOK"

View File

@@ -0,0 +1,22 @@
package com.flexcam
import android.content.Context
object FrameBus {
@Volatile
var jpeg: ByteArray? = null
}
object Pin {
private const val CHARS = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789"
fun get(ctx: Context): String {
val p = ctx.getSharedPreferences("flexcam", Context.MODE_PRIVATE)
var pin = p.getString("pin", null)
if (pin == null) {
pin = (1..4).map { CHARS.random() }.joinToString("")
p.edit().putString("pin", pin).apply()
}
return pin
}
}

View File

@@ -31,6 +31,7 @@ class WebcamService : LifecycleService() {
Config.PORT,
{ capture.latestJpeg() },
{ cmd -> if (cmd == "stop") capture.pause() else capture.resume() },
Pin.get(this),
).also { it.start() }
}

View File

@@ -36,10 +36,25 @@
android:textColor="@color/muted"
android:textSize="13sp" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="230dp"
android:layout_marginTop="26dp"
android:background="@drawable/bg_card">
<ImageView
android:id="@+id/preview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:contentDescription="@string/app_name"
android:padding="6dp"
android:scaleType="fitCenter" />
</FrameLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:layout_marginTop="14dp"
android:background="@drawable/bg_card"
android:orientation="vertical"
android:paddingHorizontal="20dp"

View File

@@ -10,6 +10,7 @@
<string name="status_perm_denied">Kamera izni reddedildi</string>
<string name="hint_with_ip">PC\'de FlexCam:\nWiFi IP: %1$s\nPort: %2$d</string>
<string name="hint_no_ip">PC\'de FlexCam\nPort: %1$d</string>
<string name="access_code">WiFi erişim kodu: %1$s</string>
<string name="notif_title">FlexCam çalışıyor</string>
<string name="notif_text">Port %1$d — USB / WiFi yayında</string>
</resources>

View File

@@ -11,6 +11,7 @@
<string name="status_perm_denied">Camera permission denied</string>
<string name="hint_with_ip">On PC (FlexCam):\nWiFi IP: %1$s\nPort: %2$d</string>
<string name="hint_no_ip">On PC (FlexCam)\nPort: %1$d</string>
<string name="access_code">WiFi access code: %1$s</string>
<string name="notif_title">FlexCam is running</string>
<string name="notif_text">Port %1$d — USB / WiFi live</string>
</resources>

View File

@@ -16,34 +16,70 @@
---
> [!CAUTION]
> **FlexCam üçün [OBS Studio](https://obsproject.com/download) quraşdırılmalıdır.**
> Telefonu veb-kamera kimi göstərmək üçün OBS Virtual Camera sürücüsündən istifadə edir.
> OBS-i bir dəfə quraşdır — açıq saxlamağa ehtiyac yoxdur.
---
## Nədir
FlexCam Android telefonunun kamerasını PC-yə ötürür və onu **"OBS Virtual
Camera"** kimi göstərir; beləliklə istənilən tətbiq (Zoom, Teams, Discord,
brauzer, OBS) onu veb-kamera kimi seçə bilər. **USB** (ADB) və **WiFi** üzərindən
işləyir və biri kəsilsə avtomatik digərinə keçir.
FlexCam Android telefonunun kamerasını PC-yə ötürür və onu **virtual veb-kamera**
kimi göstərir; beləliklə istənilən tətbiq (Zoom, Teams, Discord, brauzer, OBS)
onu kamera kimi seçə bilər. **USB** (ADB) və **WiFi** üzərindən işləyir və biri
kəsilsə avtomatik digərinə keçir.
- Hibrid **USB + WiFi**, avtomatik keçid
- Telefon hazır olanda **avtomatik qoşulur**
- **Ön / arxa** kamera dəyişimi
- **Avtomatik dik/üfüqi**
- Telefonda və PC-də **canlı önizləmə**
- **Ön / arxa** kamera, **avtomatik dik/üfüqi**
- **Ekran bağlı** ikən yayım davam edir
- **10 dil**, qaranlıq interfeys
- Tamamilə cihazında işləyir — **heç bir məlumat çıxmır**
- **WiFi giriş kodu** — şəbəkədəki yad adamlar yayımı görə bilməz
- **10 dil**, qaranlıq interfeys, trey dəstəyi
- Tamamilə cihazında işləyir — **heç bir məlumat kompüterindən çıxmır**
## Virtual kamera sürücüsü
FlexCam PC-də virtual kameraya ehtiyac duyur. İki seçimin var:
- **Daxili sürücü (OBS lazım deyil):** İlk açılışda virtual kamera tapılmasa,
tətbiqdə **"Virtual kamera quraşdır"** düyməsinə bas. Kiçik, açıq mənbəli bir
sürücünü ([Unity Capture](https://github.com/schellingb/UnityCapture), MIT)
bir dəfəlik admin icazəsi ilə qeydiyyatdan keçirir.
- **OBS Studio:** Artıq [OBS](https://obsproject.com/download) quraşdırılıbsa,
FlexCam onun virtual kamerasını avtomatik istifadə edir.
## Sürətli başlanğıc
1. **OBS Studio** quraşdır (bir dəfə).
2. Telefonda: `release/FlexCam.apk` quraşdır və tətbiqi aç.
3. PC-də: **FlexCam**-i işə sal — özü qoşulur.
4. Video tətbiqində kamera olaraq **"OBS Virtual Camera"** seç.
1. **Telefon:** `FlexCam.apk`-ı quraşdır və tətbiqi aç.
2. **PC:** **FlexCam-Windows.zip**-i aç və `FlexCam.exe`-ni işə sal.
- İlk açılış: soruşulsa **"Virtual kamera quraşdır"**a bas (bir dəfəlik).
3. USB üzərindən **avtomatik qoşulur**. WiFi üçün telefonun IP-sini və tətbiqdə
göstərilən **giriş kodunu** yaz.
4. Video tətbiqində kamera olaraq **"OBS Virtual Camera"** (daxili sürücü ilə
**"Unity Video Capture"**) seç.
> USB üçün **USB debugging** aktiv olmalıdır (Developer Options). WiFi kabel
> tələb etmir.
## Mənbədən qurmaq
**Android (APK):**
```
cd android
gradlew assembleDebug
```
> JDK 17 tələb olunur.
**PC (masaüstü tətbiqi):**
```
cd pc
python -m venv .venv
.venv\Scripts\python -m pip install -r requirements.txt
.venv\Scripts\python webgui.py
```
Müstəqil `.exe` PyInstaller (onedir) ilə — ingilis README-dəki əmrə bax.
## Texnologiya
- **Android:** Kotlin, CameraX, `8474` portunda MJPEG server.
- **PC:** Python, pywebview interfeys, `pyvirtualcam` → OBS və ya Unity Capture.
- **Ötürmə:** `adb forward` (USB) və ya telefonun IP-si (WiFi) üzərindən xam TCP.
## Lisenziya

View File

@@ -16,34 +16,71 @@
---
> [!CAUTION]
> **FlexCam benötigt [OBS Studio](https://obsproject.com/download).**
> Es nutzt die OBS Virtual Camera, um dein Handy als Webcam bereitzustellen.
> Installiere OBS einmal — du musst es nicht geöffnet lassen.
---
## Was ist das
FlexCam überträgt die Kamera deines Android-Handys auf den PC und stellt sie als
**„OBS Virtual Camera"** bereit, sodass jede App (Zoom, Teams, Discord, Browser,
OBS) sie als Webcam auswählen kann. Es funktioniert über **USB** (ADB) und
**virtuelle Webcam** bereit, sodass jede App (Zoom, Teams, Discord, Browser,
OBS) sie als Kamera auswählen kann. Es funktioniert über **USB** (ADB) und
**WLAN** und wechselt automatisch, falls eine Verbindung abbricht.
- Hybrid **USB + WLAN** mit automatischem Failover
- **Verbindet sich automatisch**, sobald das Handy bereit ist
- **Vordere / hintere** Kamera umschalten
- **Automatisch Hoch-/Querformat**
- **Live-Vorschau** auf Handy und PC
- **Vordere / hintere** Kamera, **automatisch Hoch-/Querformat**
- Streamt weiter bei **ausgeschaltetem Bildschirm**
- **10 Sprachen**, dunkle Oberfläche
- **WLAN-Zugangscode** — Fremde im Netzwerk können den Stream nicht sehen
- **10 Sprachen**, dunkle Oberfläche, Infobereich
- Läuft komplett auf deinem Gerät — **keine Daten verlassen deinen Computer**
## Treiber der virtuellen Kamera
FlexCam braucht eine virtuelle Kamera am PC. Du hast zwei Optionen:
- **Mitgelieferter Treiber (kein OBS nötig):** Wird beim ersten Start keine
virtuelle Kamera gefunden, klicke in der App auf **„Virtuelle Kamera
installieren"**. Registriert einmalig einen kleinen Open-Source-Treiber
([Unity Capture](https://github.com/schellingb/UnityCapture), MIT) mit
einmaliger Admin-Abfrage.
- **OBS Studio:** Hast du bereits [OBS](https://obsproject.com/download), nutzt
FlexCam dessen virtuelle Kamera automatisch.
## Schnellstart
1. **OBS Studio** installieren (einmalig).
2. Am Handy: `release/FlexCam.apk` installieren und App öffnen.
3. Am PC: **FlexCam** starten — es verbindet sich automatisch.
4. In deiner Video-App die Kamera **„OBS Virtual Camera"** wählen.
1. **Handy:** `FlexCam.apk` installieren und App öffnen.
2. **PC:** **FlexCam-Windows.zip** entpacken und `FlexCam.exe` starten.
- Erster Start: bei Aufforderung **„Virtuelle Kamera installieren"** (einmalig).
3. Verbindet sich per USB **automatisch**. Für WLAN die Handy-IP und den in der
App angezeigten **Zugangscode** eingeben.
4. Wähle in deiner Video-App die Kamera **„OBS Virtual Camera"** (bzw.
**„Unity Video Capture"** beim mitgelieferten Treiber).
> USB benötigt aktiviertes **USB-Debugging** (Entwickleroptionen). WLAN braucht
> kein Kabel.
## Aus Quellcode bauen
**Android (APK):**
```
cd android
gradlew assembleDebug
```
> Benötigt JDK 17.
**PC (Desktop-App):**
```
cd pc
python -m venv .venv
.venv\Scripts\python -m pip install -r requirements.txt
.venv\Scripts\python webgui.py
```
Standalone-`.exe` mit PyInstaller (onedir) — siehe den Befehl im englischen README.
## Technik
- **Android:** Kotlin, CameraX, MJPEG-Server auf Port `8474`.
- **PC:** Python, pywebview-Oberfläche, `pyvirtualcam` → OBS oder Unity Capture.
- **Übertragung:** rohes TCP über `adb forward` (USB) oder die Handy-IP (WLAN).
## Lizenz

View File

@@ -16,34 +16,71 @@
---
> [!CAUTION]
> **FlexCam requiere [OBS Studio](https://obsproject.com/download).**
> Usa la OBS Virtual Camera para exponer tu teléfono como webcam.
> Instala OBS una vez — no necesitas mantenerlo abierto.
---
## Qué es
FlexCam transmite la cámara de tu teléfono Android al PC y la expone como
**"OBS Virtual Camera"**, para que cualquier app (Zoom, Teams, Discord,
navegador, OBS) pueda elegirla como webcam. Funciona por **USB** (ADB) y
**WiFi**, y cambia automáticamente si una se cae.
FlexCam transmite la cámara de tu teléfono Android al PC y la expone como una
**webcam virtual**, para que cualquier app (Zoom, Teams, Discord, navegador,
OBS) pueda elegirla como cámara. Funciona por **USB** (ADB) y **WiFi**, y cambia
automáticamente si una se cae.
- Híbrido **USB + WiFi** con conmutación automática
- **Se conecta automáticamente** cuando el teléfono está listo
- Cambio de cámara **frontal / trasera**
- **Vertical/horizontal automático**
- **Vista previa en vivo** en el teléfono y el PC
- Cámara **frontal / trasera**, **vertical/horizontal automático**
- Sigue transmitiendo con la **pantalla apagada**
- **10 idiomas**, interfaz oscura
- **Código de acceso WiFi** — los desconocidos de tu red no ven la transmisión
- **10 idiomas**, interfaz oscura, bandeja
- Funciona totalmente en tu dispositivo — **ningún dato sale de tu ordenador**
## Controlador de cámara virtual
FlexCam necesita una cámara virtual en el PC. Tienes dos opciones:
- **Controlador incluido (sin OBS):** en el primer uso, si no se encuentra una
cámara virtual, haz clic en **"Instalar cámara virtual"** en la app. Registra
una vez un pequeño controlador de código abierto
([Unity Capture](https://github.com/schellingb/UnityCapture), MIT), con una
solicitud de administrador única.
- **OBS Studio:** si ya tienes [OBS](https://obsproject.com/download), FlexCam
usa su cámara virtual automáticamente.
## Inicio rápido
1. Instala **OBS Studio** (una vez).
2. En el teléfono: instala `release/FlexCam.apk` y abre la app.
3. En el PC: ejecuta **FlexCam** — se conecta solo.
4. En tu app de video, elige la cámara **"OBS Virtual Camera"**.
1. **Teléfono:** instala `FlexCam.apk` y abre la app.
2. **PC:** descomprime **FlexCam-Windows.zip** y ejecuta `FlexCam.exe`.
- Primer uso: si se pide, haz clic en **"Instalar cámara virtual"** (una vez).
3. Se conecta por USB **automáticamente**. Para WiFi, escribe la IP del teléfono
y el **código de acceso** que muestra la app.
4. En tu app de video, elige la cámara **"OBS Virtual Camera"** (o
**"Unity Video Capture"** con el controlador incluido).
> USB necesita la **depuración USB** activada (opciones de desarrollador). WiFi
> no necesita cable.
## Compilar desde el código
**Android (APK):**
```
cd android
gradlew assembleDebug
```
> Requiere JDK 17.
**PC (app de escritorio):**
```
cd pc
python -m venv .venv
.venv\Scripts\python -m pip install -r requirements.txt
.venv\Scripts\python webgui.py
```
`.exe` independiente con PyInstaller (onedir) — mira el comando en el README en inglés.
## Tecnología
- **Android:** Kotlin, CameraX, servidor MJPEG en el puerto `8474`.
- **PC:** Python, interfaz pywebview, `pyvirtualcam` → OBS o Unity Capture.
- **Transporte:** TCP puro vía `adb forward` (USB) o la IP del teléfono (WiFi).
## Licencia

View File

@@ -16,34 +16,72 @@
---
> [!CAUTION]
> **FlexCam nécessite [OBS Studio](https://obsproject.com/download).**
> Il utilise la caméra virtuelle d'OBS pour exposer votre téléphone comme webcam.
> Installez OBS une fois — pas besoin de le garder ouvert.
---
## Qu'est-ce que c'est
FlexCam diffuse la caméra de votre téléphone Android vers votre PC et l'expose
comme **« OBS Virtual Camera »**, afin que n'importe quelle app (Zoom, Teams,
Discord, navigateur, OBS) puisse la choisir comme webcam. Fonctionne via **USB**
comme une **webcam virtuelle**, afin que n'importe quelle app (Zoom, Teams,
Discord, navigateur, OBS) puisse la choisir comme caméra. Fonctionne via **USB**
(ADB) et **WiFi**, et bascule automatiquement si l'une tombe.
- Hybride **USB + WiFi** avec bascule automatique
- **Se connecte automatiquement** quand le téléphone est prêt
- Bascule caméra **avant / arrière**
- **Portrait/paysage automatique**
- **Aperçu en direct** sur le téléphone et le PC
- Caméra **avant / arrière**, **portrait/paysage automatique**
- Continue le flux **écran éteint**
- **10 langues**, interface sombre
- **Code d'accès WiFi** — les inconnus du réseau ne peuvent pas voir le flux
- **10 langues**, interface sombre, zone de notification
- Fonctionne entièrement sur votre appareil — **aucune donnée ne sort**
## Pilote de caméra virtuelle
FlexCam a besoin d'une caméra virtuelle sur le PC. Deux options :
- **Pilote inclus (sans OBS) :** au premier lancement, si aucune caméra
virtuelle n'est trouvée, cliquez sur **« Installer la caméra virtuelle »**
dans l'app. Il enregistre une fois un petit pilote open-source
([Unity Capture](https://github.com/schellingb/UnityCapture), MIT), avec une
demande admin unique.
- **OBS Studio :** si vous avez déjà [OBS](https://obsproject.com/download),
FlexCam utilise sa caméra virtuelle automatiquement.
## Démarrage rapide
1. Installez **OBS Studio** (une fois).
2. Sur le téléphone : installez `release/FlexCam.apk` et ouvrez l'app.
3. Sur le PC : lancez **FlexCam** — il se connecte tout seul.
4. Dans votre app vidéo, choisissez la caméra **« OBS Virtual Camera »**.
1. **Téléphone :** installez `FlexCam.apk` et ouvrez l'app.
2. **PC :** décompressez **FlexCam-Windows.zip** et lancez `FlexCam.exe`.
- Premier lancement : si demandé, cliquez sur **« Installer la caméra
virtuelle »** (une fois).
3. Se connecte en USB **automatiquement**. Pour le WiFi, saisissez l'IP du
téléphone et le **code d'accès** affiché dans l'app.
4. Dans votre app vidéo, choisissez la caméra **« OBS Virtual Camera »** (ou
**« Unity Video Capture »** avec le pilote inclus).
> L'USB nécessite le **débogage USB** activé (options développeur). Le WiFi ne
> nécessite pas de câble.
## Compiler depuis les sources
**Android (APK):**
```
cd android
gradlew assembleDebug
```
> Nécessite JDK 17.
**PC (application de bureau):**
```
cd pc
python -m venv .venv
.venv\Scripts\python -m pip install -r requirements.txt
.venv\Scripts\python webgui.py
```
`.exe` autonome avec PyInstaller (onedir) — voir la commande dans le README anglais.
## Technique
- **Android :** Kotlin, CameraX, serveur MJPEG sur le port `8474`.
- **PC :** Python, interface pywebview, `pyvirtualcam` → OBS ou Unity Capture.
- **Transport :** TCP brut via `adb forward` (USB) ou l'IP du téléphone (WiFi).
## Licence

View File

@@ -16,34 +16,71 @@
---
> [!CAUTION]
> **FlexCam के लिए [OBS Studio](https://obsproject.com/download) इंस्टॉल होना ज़रूरी है।**
> यह आपके फ़ोन को वेबकैम के रूप में दिखाने के लिए OBS Virtual Camera का उपयोग करता है।
> OBS एक बार इंस्टॉल करें — इसे खुला रखने की ज़रूरत नहीं।
---
## यह क्या है
FlexCam आपके Android फ़ोन के कैमरे को PC पर स्ट्रीम करता है और उसे
**"OBS Virtual Camera"** के रूप में दिखाता है, ताकि कोई भी ऐप (Zoom, Teams,
Discord, ब्राउज़र, OBS) उसे वेबकैम के रूप में चुन सके। यह **USB** (ADB) और
**WiFi** पर काम करता है, और एक के टूटने पर अपने आप दूसरे पर चला जाता है।
FlexCam आपके Android फ़ोन के कैमरे को PC पर स्ट्रीम करता है और उसे एक
**वर्चुअल वेबकैम** के रूप में दिखाता है, ताकि कोई भी ऐप (Zoom, Teams, Discord,
ब्राउज़र, OBS) उसे कैमरे के रूप में चुन सके। यह **USB** (ADB) और **WiFi** पर काम
करता है, और एक के टूटने पर अपने आप दूसरे पर चला जाता है।
- हाइब्रिड **USB + WiFi**, स्वतः फ़ेलओवर
- फ़ोन तैयार होने पर **स्वतः कनेक्ट**
- **आगे / पीछे** कैमरा स्विच
- **स्वतः पोर्ट्रेट/लैंडस्केप**
- फ़ोन और PC दोनों पर **लाइव प्रीव्यू**
- **आगे / पीछे** कैमरा, **स्वतः पोर्ट्रेट/लैंडस्केप**
- **स्क्रीन बंद** होने पर भी स्ट्रीमिंग जारी
- **10 भाषाएँ**, डार्क UI
- **WiFi एक्सेस कोड** — आपके नेटवर्क के अजनबी स्ट्रीम नहीं देख सकते
- **10 भाषाएँ**, डार्क UI, ट्रे समर्थन
- पूरी तरह आपके डिवाइस पर चलता है — **कोई डेटा बाहर नहीं जाता**
## वर्चुअल कैमरा ड्राइवर
FlexCam को PC पर एक वर्चुअल कैमरा चाहिए। आपके पास दो विकल्प हैं:
- **साथ में शामिल ड्राइवर (OBS की ज़रूरत नहीं):** पहली बार चलाने पर अगर कोई
वर्चुअल कैमरा न मिले, तो ऐप में **"वर्चुअल कैमरा इंस्टॉल करें"** पर क्लिक करें।
यह एक छोटा ओपन-सोर्स ड्राइवर
([Unity Capture](https://github.com/schellingb/UnityCapture), MIT) एक बार,
एक व्यवस्थापक अनुमति के साथ, रजिस्टर करता है।
- **OBS Studio:** अगर आपके पास पहले से [OBS](https://obsproject.com/download) है,
तो FlexCam उसका वर्चुअल कैमरा अपने आप उपयोग करता है।
## जल्दी शुरू करें
1. **OBS Studio** इंस्टॉल करें (एक बार)
2. फ़ोन पर: `release/FlexCam.apk` इंस्टॉल करें और ऐप खोलें
3. PC पर: **FlexCam** चलाएँ — यह अपने आप कनेक्ट हो जाता है
4. अपने वीडियो ऐप में कैमरा **"OBS Virtual Camera"** चुनें।
1. **फ़ोन:** `FlexCam.apk` इंस्टॉल करें और ऐप खोलें
2. **PC:** **FlexCam-Windows.zip** को अनज़िप करें और `FlexCam.exe` चलाएँ
- पहली बार: पूछे जाने पर **"वर्चुअल कैमरा इंस्टॉल करें"** पर क्लिक करें (एक बार)
3. USB से **स्वतः कनेक्ट** होता है। WiFi के लिए फ़ोन का IP और ऐप में दिखाया गया
**एक्सेस कोड** लिखें।
4. अपने वीडियो ऐप में कैमरा **"OBS Virtual Camera"** (शामिल ड्राइवर के साथ
**"Unity Video Capture"**) चुनें।
> USB के लिए **USB डिबगिंग** चालू होना चाहिए (डेवलपर विकल्प)। WiFi को केबल की
> ज़रूरत नहीं।
## सोर्स से बनाएँ
**Android (APK):**
```
cd android
gradlew assembleDebug
```
> JDK 17 आवश्यक है।
**PC (डेस्कटॉप ऐप):**
```
cd pc
python -m venv .venv
.venv\Scripts\python -m pip install -r requirements.txt
.venv\Scripts\python webgui.py
```
PyInstaller (onedir) से स्टैंडअलोन `.exe` — अंग्रेज़ी README में कमांड देखें।
## तकनीक
- **Android:** Kotlin, CameraX, पोर्ट `8474` पर MJPEG सर्वर।
- **PC:** Python, pywebview UI, `pyvirtualcam` → OBS या Unity Capture।
- **ट्रांसपोर्ट:** `adb forward` (USB) या फ़ोन के IP (WiFi) पर रॉ TCP।
## लाइसेंस

View File

@@ -16,34 +16,70 @@
---
> [!CAUTION]
> **FlexCam には [OBS Studio](https://obsproject.com/download) が必要です。**
> スマホをウェブカメラとして見せるために OBS 仮想カメラを使います。
> OBS は一度インストールするだけ — 開いたままにする必要はありません。
---
## これは何
FlexCam は Android スマホのカメラを PC にストリーミングし、**「OBS Virtual
Camera」** として見せます。これにより、どのアプリZoom、Teams、Discord、
ブラウザ、OBSでもウェブカメラとして選べます。**USB**ADB**WiFi**
動作し、片方が切れると自動で切り替わります。
FlexCam は Android スマホのカメラを PC にストリーミングし、**仮想ウェブカメラ**
として見せます。これにより、どのアプリZoom、Teams、Discord、ブラウザ、OBS
でもカメラとして選べます。**USB**ADB**WiFi**動作し、片方が切れると
自動で切り替わります。
- **USB + WiFi** ハイブリッド、自動フェイルオーバー
- スマホの準備ができると**自動接続**
- **前面 / 背面**カメラ切り替え
- **自動で縦/横**
- スマホと PC の両方で**ライブプレビュー**
- **前面 / 背面**カメラ、**自動で縦/横**
- **画面オフ**でも配信継続
- **10 言語**、ダーク UI
- **WiFi アクセスコード** — ネットワーク上の他人は映像を見られません
- **10 言語**、ダーク UI、トレイ対応
- すべて端末内で動作 — **データがパソコンから出ることはありません**
## 仮想カメラドライバー
FlexCam は PC 上の仮想カメラが必要です。2 つの方法があります:
- **同梱ドライバーOBS 不要):** 初回起動時に仮想カメラが見つからない場合、
アプリの **「仮想カメラをインストール」** をクリック。小さなオープンソース
ドライバー([Unity Capture](https://github.com/schellingb/UnityCapture)、MIT
を一度だけ、管理者確認付きで登録します。
- **OBS Studio** すでに [OBS](https://obsproject.com/download) がある場合、
FlexCam はその仮想カメラを自動的に使います。
## クイックスタート
1. **OBS Studio** をインストール(一度)
2. スマホ:`release/FlexCam.apk`インストールしてアプリを開く
3. PC**FlexCam** を起動 — 自動で接続します
4. ビデオアプリでカメラ **「OBS Virtual Camera」** を選択。
1. **スマホ:** `FlexCam.apk` をインストールしてアプリを開く
2. **PC** **FlexCam-Windows.zip** を解凍し `FlexCam.exe`起動
- 初回:求められたら **「仮想カメラをインストール」**(一度だけ)
3. USB で**自動接続**します。WiFi の場合はスマホの IP とアプリに表示される
**アクセスコード**を入力。
4. ビデオアプリでカメラ **「OBS Virtual Camera」**(同梱ドライバー使用時は
**「Unity Video Capture」**)を選択。
> USB は**USB デバッグ**の有効化が必要開発者向けオプション。WiFi はケーブル
> 不要。
## ソースからビルド
**Android (APK):**
```
cd android
gradlew assembleDebug
```
> JDK 17 が必要。
**PCデスクトップアプリ**
```
cd pc
python -m venv .venv
.venv\Scripts\python -m pip install -r requirements.txt
.venv\Scripts\python webgui.py
```
PyInstalleronedirでスタンドアロン `.exe` — 英語 README のコマンドを参照。
## 技術
- **Android** Kotlin、CameraX、ポート `8474` の MJPEG サーバー。
- **PC** Python、pywebview UI、`pyvirtualcam` → OBS または Unity Capture。
- **転送:** `adb forward`USBまたはスマホの IPWiFi経由の生 TCP。
## ライセンス

View File

@@ -16,34 +16,71 @@
---
> [!CAUTION]
> **O FlexCam requer o [OBS Studio](https://obsproject.com/download).**
> Ele usa a OBS Virtual Camera para expor seu telefone como webcam.
> Instale o OBS uma vez — não precisa mantê-lo aberto.
---
## O que é
O FlexCam transmite a câmera do seu telefone Android para o PC e a expõe como
**"OBS Virtual Camera"**, para que qualquer app (Zoom, Teams, Discord, navegador,
OBS) possa escolhê-la como webcam. Funciona via **USB** (ADB) e **WiFi**, e
alterna automaticamente se uma cair.
O FlexCam transmite a câmera do seu telefone Android para o PC e a expõe como uma
**webcam virtual**, para que qualquer app (Zoom, Teams, Discord, navegador, OBS)
possa escolhê-la como câmera. Funciona via **USB** (ADB) e **WiFi**, e alterna
automaticamente se uma cair.
- Híbrido **USB + WiFi** com troca automática
- **Conecta automaticamente** quando o telefone está pronto
- Troca de câmera **frontal / traseira**
- **Retrato/paisagem automático**
- **Pré-visualização ao vivo** no telefone e no PC
- Câmera **frontal / traseira**, **retrato/paisagem automático**
- Continua transmitindo com a **tela desligada**
- **10 idiomas**, interface escura
- **Código de acesso WiFi** — estranhos na sua rede não veem a transmissão
- **10 idiomas**, interface escura, bandeja
- Roda totalmente no seu dispositivo — **nenhum dado sai do seu computador**
## Driver de câmera virtual
O FlexCam precisa de uma câmera virtual no PC. Você tem duas opções:
- **Driver incluído (sem OBS):** no primeiro uso, se nenhuma câmera virtual for
encontrada, clique em **"Instalar câmera virtual"** no app. Ele registra uma
vez um pequeno driver de código aberto
([Unity Capture](https://github.com/schellingb/UnityCapture), MIT), com um
pedido de administrador único.
- **OBS Studio:** se você já tem o [OBS](https://obsproject.com/download), o
FlexCam usa a câmera virtual dele automaticamente.
## Início rápido
1. Instale o **OBS Studio** (uma vez).
2. No telefone: instale `release/FlexCam.apk` e abra o app.
3. No PC: execute o **FlexCam** — ele conecta sozinho.
4. No seu app de vídeo, escolha a câmera **"OBS Virtual Camera"**.
1. **Telefone:** instale `FlexCam.apk` e abra o app.
2. **PC:** extraia **FlexCam-Windows.zip** e execute `FlexCam.exe`.
- Primeiro uso: se solicitado, clique em **"Instalar câmera virtual"** (uma vez).
3. Conecta por USB **automaticamente**. Para WiFi, digite o IP do telefone e o
**código de acesso** mostrado no app.
4. No seu app de vídeo, escolha a câmera **"OBS Virtual Camera"** (ou
**"Unity Video Capture"** com o driver incluído).
> USB precisa de **depuração USB** ativada (Opções do desenvolvedor). WiFi não
> precisa de cabo.
## Compilar do código-fonte
**Android (APK):**
```
cd android
gradlew assembleDebug
```
> Requer JDK 17.
**PC (app de desktop):**
```
cd pc
python -m venv .venv
.venv\Scripts\python -m pip install -r requirements.txt
.venv\Scripts\python webgui.py
```
`.exe` independente com PyInstaller (onedir) — veja o comando no README em inglês.
## Tecnologia
- **Android:** Kotlin, CameraX, servidor MJPEG na porta `8474`.
- **PC:** Python, interface pywebview, `pyvirtualcam` → OBS ou Unity Capture.
- **Transporte:** TCP puro via `adb forward` (USB) ou o IP do telefone (WiFi).
## Licença

View File

@@ -16,37 +16,71 @@
---
> [!CAUTION]
> **FlexCam'in çalışması için [OBS Studio](https://obsproject.com/download) kurulu olmalıdır.**
> Telefonu webcam olarak göstermek için OBS Virtual Camera sürücüsünü kullanır.
> OBS'i bir kez kur — açık tutmana gerek yok.
---
## Nedir
FlexCam, Android telefonunun kamerasını PC'ne aktarır ve **"OBS Virtual Camera"**
FlexCam, Android telefonunun kamerasını PC'ne aktarır ve bir **sanal webcam**
olarak gösterir; böylece herhangi bir uygulama (Zoom, Teams, Discord, tarayıcı,
OBS) onu webcam olarak seçebilir. **USB** (ADB) ve **WiFi** üzerinden çalışır,
OBS) onu kamera olarak seçebilir. **USB** (ADB) ve **WiFi** üzerinden çalışır,
biri koparsa otomatik olarak diğerine geçer.
- Hibrit **USB + WiFi**, otomatik yük devretme
- Telefon hazır olunca **otomatik bağlanır** — elle işlem yok
- **Ön / arka** kamera geçişi
- **Otomatik dik/yatay** (telefonun yönünü izler)
- Telefonda ve PC'de **canlı önizleme**
- **Ön / arka** kamera geçişi, **otomatik dik/yatay**
- **Ekran kapalıyken** de yayın sürer
- **WiFi erişim kodu** — ağdaki yabancılar yayınını izleyemez
- **10 dil**, koyu arayüz, tepsi desteği
- Tamamen cihazında çalışır — **hiçbir veri dışarı gönderilmez**
## Sanal kamera sürücüsü
FlexCam'in PC'de bir sanal kameraya ihtiyacı var. İki seçeneğin var:
- **Gömülü sürücü (OBS gerekmez):** İlk açılışta sanal kamera bulunamazsa,
uygulamadaki **"Sanal kamera kur"** düğmesine tıkla. Küçük, açık kaynak bir
sürücüyü ([Unity Capture](https://github.com/schellingb/UnityCapture), MIT)
tek seferlik yönetici onayıyla kaydeder.
- **OBS Studio:** Zaten [OBS](https://obsproject.com/download) kuruluysa FlexCam
onun sanal kamerasını otomatik kullanır.
## Hızlı başlangıç
1. **OBS Studio** kur (bir kez).
2. Telefonda: `release/FlexCam.apk` kur ve uygulamayı.
3. PC'de: **FlexCam**'i çalıştır, otomatik bağlanır.
4. Video uygulamanda kamera olarak **"OBS Virtual Camera"** seç.
1. **Telefon:** `FlexCam.apk`'yı kur ve uygulamayı.
2. **PC:** **FlexCam-Windows.zip**'i aç ve `FlexCam.exe`'yi çalıştır.
- İlk açılış: istenirse **"Sanal kamera kur"**a tıkla (tek seferlik).
3. USB üzerinden **otomatik bağlanır**. WiFi için telefonun IP'sini ve
uygulamada görünen **erişim kodunu** yaz.
4. Video uygulamanda kamera olarak **"OBS Virtual Camera"** (gömülü sürücü
kullandıysan **"Unity Video Capture"**) seç.
USB için **USB hata ayıklama**ık olmalı (Geliştirici Seçenekleri). WiFi kablo
istemez — uygulamanın gösterdiği IP'yi yaz.
> USB için **USB hata ayıklama** açık olmalı (Geliştirici Seçenekleri).
> WiFi kablo istemez.
## Kaynaktan derleme
**Android (APK):**
```
cd android
gradlew assembleDebug
```
> JDK 17 gerekir.
**PC (masaüstü uygulaması):**
```
cd pc
python -m venv .venv
.venv\Scripts\python -m pip install -r requirements.txt
.venv\Scripts\python webgui.py
```
Bağımsız `.exe` paketlemek (PyInstaller, onedir): İngilizce README'deki komuta
bak.
## Teknik
- **Android:** Kotlin, CameraX, `8474` portunda MJPEG sunucu.
- **PC:** Python, pywebview arayüz, `pyvirtualcam` → OBS veya Unity Capture.
- **Aktarım:** `adb forward` (USB) ya da telefonun IP'si (WiFi) üzerinden ham TCP.
## Lisans

View File

@@ -16,33 +16,67 @@
---
> [!CAUTION]
> **FlexCam 需要安装 [OBS Studio](https://obsproject.com/download)。**
> 它使用 OBS 虚拟摄像头把手机显示为网络摄像头。
> 安装一次 OBS 即可 — 无需保持打开。
---
## 这是什么
FlexCam 把安卓手机的摄像头串流到电脑,并显示为 **“OBS Virtual Camera”**
这样任何应用Zoom、Teams、Discord、浏览器、OBS都能把它当作网络摄像头。
FlexCam 把安卓手机的摄像头串流到电脑,并显示为一个**虚拟网络摄像头**
这样任何应用Zoom、Teams、Discord、浏览器、OBS都能把它当作摄像头。
它通过 **USB**ADB**WiFi** 工作,其中一个断开时会自动切换。
- **USB + WiFi** 混合,自动切换
- 手机就绪时**自动连接**
- **前置 / 后置**摄像头切换
- **自动竖屏/横屏**
- 手机和电脑上都有**实时预览**
- **前置 / 后置**摄像头,**自动竖屏/横屏**
- **息屏**时继续串流
- **10 种语言**,深色界
- **WiFi 访问码** — 网络中的陌生人无法查看你的画
- **10 种语言**,深色界面,托盘支持
- 完全在你的设备上运行 — **没有任何数据离开你的电脑**
## 虚拟摄像头驱动
FlexCam 需要电脑上的虚拟摄像头。你有两个选择:
- **内置驱动(无需 OBS** 首次运行时如果未找到虚拟摄像头,点击应用中的
**“安装虚拟摄像头”**。它会一次性注册一个小型开源驱动
[Unity Capture](https://github.com/schellingb/UnityCapture)MIT
需要一次管理员确认。
- **OBS Studio** 如果你已安装 [OBS](https://obsproject.com/download)
FlexCam 会自动使用它的虚拟摄像头。
## 快速开始
1. 安装 **OBS Studio**(一次)
2. 手机上:安装 `release/FlexCam.apk` 并打开应用
3. 电脑上:运行 **FlexCam** — 它会自动连接
4. 在你的视频应用中,选择摄像头 **“OBS Virtual Camera”**。
1. **手机:** 安装 `FlexCam.apk` 并打开应用
2. **电脑:** 解压 **FlexCam-Windows.zip** 并运行 `FlexCam.exe`
- 首次运行:如有提示,点击**“安装虚拟摄像头”**(仅一次)
3. 通过 USB **自动连接**。使用 WiFi 时,输入手机 IP 和应用中显示的**访问码**。
4. 在你的视频应用中,选择摄像头 **“OBS Virtual Camera”**
(若使用内置驱动则为 **“Unity Video Capture”**)。
> USB 需要开启**USB 调试**开发者选项。WiFi 不需要线缆。
## 从源码构建
**Android (APK):**
```
cd android
gradlew assembleDebug
```
> 需要 JDK 17。
**PC桌面应用**
```
cd pc
python -m venv .venv
.venv\Scripts\python -m pip install -r requirements.txt
.venv\Scripts\python webgui.py
```
用 PyInstalleronedir打包独立 `.exe` — 见英文 README 中的命令。
## 技术
- **Android** Kotlin、CameraX端口 `8474` 上的 MJPEG 服务器。
- **PC** Python、pywebview 界面、`pyvirtualcam` → OBS 或 Unity Capture。
- **传输:** 通过 `adb forward`USB或手机 IPWiFi的原始 TCP。
## 许可证

View File

@@ -9,9 +9,13 @@ import adb_tools
PORT = 8474
def _stream(host, timeout=2):
def _q(key):
return f"?key={key}" if key else ""
def _stream(host, key="", timeout=2):
conn = HTTPConnection(host, PORT, timeout=timeout)
conn.request("GET", "/")
conn.request("GET", "/" + _q(key))
resp = conn.getresponse()
if resp.status != 200:
raise RuntimeError(f"HTTP {resp.status}")
@@ -25,10 +29,10 @@ def _stream(host, timeout=2):
yield jpeg
def _control(host, cmd):
def _control(host, cmd, key=""):
try:
conn = HTTPConnection(host, PORT, timeout=1.5)
conn.request("GET", "/" + cmd)
conn.request("GET", "/" + cmd + _q(key))
conn.getresponse().read()
conn.close()
except Exception:
@@ -42,13 +46,21 @@ class FlexCamEngine:
self._thread = None
self._running = False
self._active_host = None
self._key = ""
self._preview_jpeg = None
def is_running(self):
return self._running
def preview_jpeg(self):
return self._preview_jpeg
def set_wifi_ip(self, ip):
self._wifi_ip = ip
def set_key(self, key):
self._key = (key or "").strip()
def start(self):
if self._running:
return
@@ -95,26 +107,29 @@ class FlexCamEngine:
if src["usb"]:
if not adb_tools.ensure_forward():
continue
_control(src["host"], "start")
for jpeg in _stream(src["host"]):
_control(src["host"], "start", self._key)
for jpeg in _stream(src["host"], self._key):
if not self._running:
break
if active != src["name"]:
active = src["name"]
self._active_host = src["host"]
self._on_status("connected", active, "")
self._preview_jpeg = jpeg
cam.send(jpeg_to_rgb(jpeg))
active = None
self._preview_jpeg = None
except Exception:
if active == src["name"]:
self._on_status("searching", None, "")
active = None
self._preview_jpeg = None
if self._running:
time.sleep(1)
finally:
cam.close()
if self._active_host:
_control(self._active_host, "stop")
_control(self._active_host, "stop", self._key)
self._active_host = None
self._running = False
self._on_status("stopped", None, "")

View File

@@ -38,6 +38,9 @@ STRINGS = {
"install_obs": "Install OBS",
"obs_ready": "OBS ready",
"obs_missing": "OBS Studio is required",
"vcam_ready": "Virtual camera ready",
"install_vcam": "Install virtual camera",
"vcam_installing": "Installing driver — approve the admin prompt.",
"phone_ready": "Phone app ready",
"settings": "Settings",
"setting_tray": "Minimize to tray on close",
@@ -74,6 +77,9 @@ STRINGS = {
"install_obs": "OBS'yi Kur",
"obs_ready": "OBS hazır",
"obs_missing": "OBS Studio gerekli",
"vcam_ready": "Sanal kamera hazır",
"install_vcam": "Sanal kamera kur",
"vcam_installing": "Sürücü kuruluyor — yönetici iznini onayla.",
"phone_ready": "Uygulama kurulu",
"settings": "Ayarlar",
"setting_tray": "Kapatınca tepsiye küçült",

54
pc/preview_server.py Normal file
View File

@@ -0,0 +1,54 @@
import threading
import time
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
PREVIEW_PORT = 8475
class PreviewServer:
def __init__(self, get_frame):
self._get = get_frame
self._httpd = None
def start(self):
get = self._get
class Handler(BaseHTTPRequestHandler):
def log_message(self, *a):
pass
def do_GET(self):
if self.path.startswith("/preview"):
self._stream()
else:
self.send_response(404)
self.end_headers()
def _stream(self):
try:
self.send_response(200)
self.send_header(
"Content-Type",
"multipart/x-mixed-replace; boundary=frame",
)
self.send_header("Cache-Control", "no-cache")
self.end_headers()
while True:
jpeg = get()
if jpeg:
self.wfile.write(b"--frame\r\n")
self.wfile.write(b"Content-Type: image/jpeg\r\n")
self.wfile.write(
b"Content-Length: " + str(len(jpeg)).encode() + b"\r\n\r\n"
)
self.wfile.write(jpeg)
self.wfile.write(b"\r\n")
time.sleep(0.05)
except Exception:
pass
try:
self._httpd = ThreadingHTTPServer(("127.0.0.1", PREVIEW_PORT), Handler)
threading.Thread(target=self._httpd.serve_forever, daemon=True).start()
except Exception:
pass

View File

@@ -1,11 +1,34 @@
import json
import os
import sys
import webbrowser
from urllib.request import urlopen
import config
def _base_dir():
if getattr(sys, "frozen", False):
return getattr(sys, "_MEIPASS", os.path.dirname(sys.executable))
return os.path.dirname(os.path.abspath(__file__))
def virtualcam_installer():
p = os.path.join(_base_dir(), "virtualcam", "Install.bat")
return p if os.path.exists(p) else None
def install_virtualcam():
p = virtualcam_installer()
if not p:
return False
try:
os.startfile(p)
return True
except Exception:
return False
def open_url(url):
try:
webbrowser.open(url)

View File

@@ -26,7 +26,17 @@ class VirtualCamera:
def __init__(self, width: int = 1280, height: int = 720, fps: int = 30):
self._w = width
self._h = height
self._cam = pyvirtualcam.Camera(width=width, height=height, fps=fps)
self._cam = None
for backend in ("obs", "unitycapture"):
try:
self._cam = pyvirtualcam.Camera(
width=width, height=height, fps=fps, backend=backend
)
break
except Exception:
continue
if self._cam is None:
self._cam = pyvirtualcam.Camera(width=width, height=height, fps=fps)
@property
def device(self) -> str:

28
pc/virtualcam/Install.bat Normal file
View File

@@ -0,0 +1,28 @@
@echo off
:: BatchGotAdmin
:-------------------------------------
REM --> Check for permissions
>nul 2>&1 "%SYSTEMROOT%\system32\cacls.exe" "%SYSTEMROOT%\system32\config\system"
REM --> If error flag set, we do not have admin.
if '%errorlevel%' NEQ '0' (
echo Requesting administrative privileges...
goto UACPrompt
) else ( goto gotAdmin )
:UACPrompt
echo Set UAC = CreateObject^("Shell.Application"^) > "%temp%\getadmin.vbs"
set params = %*:"=""
echo UAC.ShellExecute "cmd.exe", "/c %~s0 %params%", "", "runas", 1 >> "%temp%\getadmin.vbs"
"%temp%\getadmin.vbs"
del "%temp%\getadmin.vbs"
exit /B
:gotAdmin
pushd "%CD%"
CD /D "%~dp0"
regsvr32 "UnityCaptureFilter32.dll"
regsvr32 "UnityCaptureFilter64.dll"
:--------------------------------------

View File

@@ -0,0 +1 @@
404: Not Found

View File

@@ -0,0 +1,28 @@
@echo off
:: BatchGotAdmin
:-------------------------------------
REM --> Check for permissions
>nul 2>&1 "%SYSTEMROOT%\system32\cacls.exe" "%SYSTEMROOT%\system32\config\system"
REM --> If error flag set, we do not have admin.
if '%errorlevel%' NEQ '0' (
echo Requesting administrative privileges...
goto UACPrompt
) else ( goto gotAdmin )
:UACPrompt
echo Set UAC = CreateObject^("Shell.Application"^) > "%temp%\getadmin.vbs"
set params = %*:"=""
echo UAC.ShellExecute "cmd.exe", "/c %~s0 %params%", "", "runas", 1 >> "%temp%\getadmin.vbs"
"%temp%\getadmin.vbs"
del "%temp%\getadmin.vbs"
exit /B
:gotAdmin
pushd "%CD%"
CD /D "%~dp0"
regsvr32 /u "UnityCaptureFilter32.dll"
regsvr32 /u "UnityCaptureFilter64.dll"
:--------------------------------------

Binary file not shown.

Binary file not shown.

View File

@@ -70,7 +70,7 @@ async function setLang(code) {
S = r.strings;
CFG.lang = r.lang;
applyStrings();
refreshObs(CFG.obs_installed);
refreshVcam();
setLangButton(CFG.langs, r.lang);
buildLangMenu(CFG.langs, r.lang);
pollUsb();
@@ -80,6 +80,8 @@ async function setLang(code) {
function setStatus(state, info) {
lastState = state;
lastInfo = info;
const pc = document.querySelector(".preview-card");
if (pc) pc.classList.toggle("live", state === "connected");
const dot = $("dot");
dot.className = "dot " + (state || "");
let text;
@@ -105,14 +107,14 @@ function setLog(text, cls) {
l.className = "log " + (cls || "");
}
function refreshObs(installed) {
function refreshVcam() {
const label = $("obsLabel");
const btn = $("obsBtn");
if (installed) {
label.textContent = "✓ " + S.obs_ready;
if (CFG.vcam_ready) {
label.textContent = "✓ " + S.vcam_ready;
btn.classList.remove("warn");
} else {
label.textContent = S.install_obs;
label.textContent = S.install_vcam;
btn.classList.add("warn");
}
}
@@ -124,7 +126,7 @@ async function init() {
applyStrings();
$("versionLbl").textContent = "v" + boot.version;
$("trayChk").checked = !!boot.tray;
refreshObs(boot.obs_installed);
refreshVcam();
setRunning(boot.running);
setLangButton(boot.langs, boot.lang);
@@ -140,7 +142,19 @@ async function init() {
$("navDiscord").onclick = () => window.pywebview.api.open_url(CFG.discord);
$("navInsta").onclick = () => window.pywebview.api.open_url(CFG.instagram);
$("navDonate").onclick = () => window.pywebview.api.open_url(CFG.donate);
$("obsBtn").onclick = () => window.pywebview.api.open_url(CFG.obs_url);
$("obsBtn").onclick = async () => {
if (CFG.vcam_ready) return;
if (CFG.has_installer) {
setLog(S.vcam_installing, "");
const ok = await window.pywebview.api.install_virtualcam();
if (ok) {
CFG.vcam_ready = true;
refreshVcam();
}
} else {
window.pywebview.api.open_url(CFG.obs_url);
}
};
$("trayChk").onchange = (e) =>
window.pywebview.api.save_setting("tray", e.target.checked);
@@ -151,7 +165,7 @@ async function init() {
} else {
setRunning(true);
setStatus("starting", null);
window.pywebview.api.start($("wifiIp").value.trim());
window.pywebview.api.start($("wifiIp").value.trim(), $("wifiKey").value.trim());
}
};
@@ -175,10 +189,12 @@ async function init() {
pollUsb();
setInterval(pollUsb, 3000);
$("previewImg").src = "http://127.0.0.1:8475/preview";
$("wifiIp").value = boot.wifi_ip || "";
$("wifiKey").value = boot.wifi_key || "";
setRunning(true);
setStatus("starting", null);
window.pywebview.api.start(boot.wifi_ip || "");
window.pywebview.api.start(boot.wifi_ip || "", boot.wifi_key || "");
}
window.flexStatus = setStatus;

View File

@@ -78,6 +78,11 @@
<main class="main">
<p class="tagline" id="tagline">Turn your phone into a webcam</p>
<div class="preview-card">
<img id="previewImg" class="preview-img" alt="">
<span class="preview-dot"></span>
</div>
<div class="status-card">
<span class="status-caption" id="statusCaption">Connection</span>
<div class="status-main">
@@ -93,7 +98,10 @@
<div class="field">
<label id="wifiLabel">WiFi IP (optional)</label>
<input type="text" id="wifiIp" placeholder="192.168.1.x">
<div class="ip-row">
<input type="text" id="wifiIp" placeholder="192.168.1.x">
<input type="text" id="wifiKey" placeholder="Kod" maxlength="8">
</div>
</div>
<button class="primary" id="toggleBtn">Start</button>

View File

@@ -28,16 +28,17 @@ body {
.app { display: flex; height: 100vh; }
.sidebar {
width: 230px;
width: 252px;
background: var(--sidebar);
display: flex;
flex-direction: column;
padding: 18px 14px;
transition: width 0.22s ease;
overflow: hidden;
flex-shrink: 0;
}
.sidebar.collapsed { width: 68px; }
.sidebar.collapsed { width: 72px; }
.side-top { display: flex; align-items: center; gap: 10px; margin-bottom: 26px; }
@@ -63,19 +64,23 @@ body {
display: flex;
align-items: center;
gap: 12px;
width: 100%;
min-width: 0;
text-align: left;
background: transparent;
border: none;
color: var(--muted);
font: inherit;
font-size: 14px;
font-size: 13.5px;
padding: 11px 12px;
border-radius: 12px;
cursor: pointer;
white-space: nowrap;
transition: background 0.15s, color 0.15s;
}
.nav-item:hover { background: var(--card); color: var(--fg); }
.nav-item svg, .side-btn svg { flex-shrink: 0; }
.nav-item .label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.side-btn .label { flex: 1; min-width: 0; line-height: 1.25; }
.side-actions {
margin-top: 22px;
@@ -217,6 +222,30 @@ body {
.tagline { color: var(--muted); font-size: 15px; }
.preview-card {
position: relative;
width: 100%;
max-width: 360px;
aspect-ratio: 16 / 10;
background: #0c0e15;
border: 1px solid #2a2f40;
border-radius: 14px;
overflow: hidden;
}
.preview-img { width: 100%; height: 100%; object-fit: contain; display: block; }
.preview-dot {
position: absolute;
top: 10px; right: 10px;
width: 9px; height: 9px; border-radius: 50%;
background: var(--gray);
box-shadow: 0 0 0 4px rgba(107,114,128,.15);
transition: background .2s, box-shadow .2s;
}
.preview-card.live .preview-dot {
background: var(--green);
box-shadow: 0 0 0 4px rgba(34,197,94,.18);
}
.status-card {
display: flex;
flex-direction: column;
@@ -273,6 +302,9 @@ body {
.field { width: 100%; max-width: 360px; }
.field label { display: block; font-size: 12px; color: var(--muted); margin-bottom: 6px; }
.ip-row { display: flex; gap: 8px; }
.ip-row #wifiIp { flex: 1; }
.ip-row #wifiKey { width: 96px; text-align: center; letter-spacing: 1px; }
.field input {
width: 100%;
background: var(--card);

View File

@@ -11,6 +11,7 @@ import sysutil
import config
import i18n
import presence
import preview_server
def base_dir():
@@ -57,9 +58,12 @@ class Api:
"obs_url": config.OBS_DOWNLOAD_URL,
"version": config.APP_VERSION,
"obs_installed": sysutil.obs_installed(),
"vcam_ready": sysutil.obs_installed() or self.settings.get("vcam_installed", False),
"has_installer": sysutil.virtualcam_installer() is not None,
"tray": self.settings.get("tray", True),
"running": self.engine.is_running(),
"wifi_ip": self.settings.get("wifi_ip", ""),
"wifi_key": self.settings.get("wifi_key", ""),
}
def set_lang(self, lang):
@@ -68,12 +72,14 @@ class Api:
sysutil.save_settings(self.settings)
return {"strings": self._strings(), "lang": self.t.lang}
def start(self, ip):
def start(self, ip, key=""):
ip = (ip or "").strip()
if ip:
self.settings["wifi_ip"] = ip
sysutil.save_settings(self.settings)
self.engine.set_wifi_ip(ip or self.settings.get("wifi_ip") or None)
key = (key or "").strip()
self.settings["wifi_ip"] = ip
self.settings["wifi_key"] = key
sysutil.save_settings(self.settings)
self.engine.set_wifi_ip(ip or None)
self.engine.set_key(key)
self.engine.start()
def stop(self):
@@ -86,6 +92,13 @@ class Api:
def open_url(self, url):
sysutil.open_url(url)
def install_virtualcam(self):
ok = sysutil.install_virtualcam()
if ok:
self.settings["vcam_installed"] = True
sysutil.save_settings(self.settings)
return ok
def usb_connected(self):
return adb_tools.detect_device() is not None
@@ -170,12 +183,13 @@ def main():
api = Api()
window = webview.create_window(
"FlexCam", url=web_index(), js_api=api,
width=760, height=520, min_size=(680, 480),
width=1120, height=760, min_size=(900, 640),
background_color="#14161f",
)
_WINDOW = window
tray = TrayHolder(api, window)
window.events.closing += tray.on_closing
preview_server.PreviewServer(lambda: api.engine.preview_jpeg()).start()
presence.start()
webview.start()

Binary file not shown.