go2rtc ശൈലിയിലുള്ള ക്യാമറ പ്രോട്ടോക്കോളുകൾ, ലൈബ്രറികളായി
kotlin2rtc: Android-നും JVM-നുമുള്ള ONVIF, RTSP
കൊറൂട്ടീനുകളോടുകൂടിയ സാധാരണ Kotlin, Android ആശ്രിതത്വം ഇല്ല, അതിനാൽ Android ആപ്പിലും ഏത് JVM-ലും ഇത് പ്രവർത്തിക്കുന്നു (Java 17-നുള്ള ബൈറ്റ്കോഡ്). നാല് മോഡ്യൂളുകൾ:
ഇൻസ്റ്റാൾ ചെയ്യുക
റിലീസുകൾ പ്രോജക്റ്റിന്റെ GitHub Releases-ലെ ഫയലുകളായാണ് പ്രസിദ്ധീകരിക്കുന്നത് (0.1.1 ആണ് ഏറ്റവും പുതിയത്), Maven Central-ൽ അല്ല: അവിടേക്ക് ചൂണ്ടുന്ന ഒരു Ivy റിപ്പോസിറ്ററി വഴി Gradle അവ ഡൗൺലോഡ് ചെയ്യുന്നു. ഡൗൺലോഡ് ചെയ്യുന്നത് Gradle-ന്റെ ഡിപൻഡൻസി വെരിഫിക്കേഷൻ ഉപയോഗിച്ച് പിൻ ചെയ്യുകയും റിലീസിലെ SHA256SUMS-മായി താരതമ്യം ചെയ്യുകയും ചെയ്യുക.
// settings.gradle.kts: releases are files of GitHub Releases, resolved through an Ivy repository
dependencyResolutionManagement {
repositories {
mavenCentral() // kotlinx-coroutines and the Kotlin stdlib
ivy {
name = "kotlin2rtc"
url = uri("https://github.com/mafischer/kotlin2rtc/releases/download")
patternLayout { artifact("v[revision]/[artifact]-[revision](-[classifier])(.[ext])") }
metadataSources { gradleMetadata() }
content { includeGroup("io.github.mafischer") }
}
}
}
// build.gradle.kts
dependencies {
implementation("io.github.mafischer:kotlin2rtc-onvif:0.1.1")
implementation("io.github.mafischer:kotlin2rtc-rtsp:0.1.1") // audio backchannel; brings -codec and -core
}ഒരു ക്യാമറ കണ്ടെത്തി അതിന്റെ സ്ട്രീം നേടുക
import io.github.mafischer.kotlin2rtc.core.*
import io.github.mafischer.kotlin2rtc.onvif.*
// Only addresses on this device's subnets may be called (no cleartext SOAP to the internet)
val network = LocalNetwork(listOf(LinkSubnet.parse("192.168.1.20/24")))
// WS-Discovery: the ONVIF cameras that answer on this network
val cameras = WsDiscovery(accept = network::allows).discover(timeoutMillis = 3_000)
val onvif = OnvifClient(
transport = LanHttpTransport(allows = network::allowsCamera),
deviceUrl = cameras.first().deviceUrl,
credentials = Credentials("camera-user", "camera-password"),
)
onvif.syncClock() // WS-Security timestamps follow the camera's clock
val endpoints = onvif.endpoints()
val profile = onvif.profiles(endpoints.mediaUrl).first { it.hasVideo }
val rtspUrl = onvif.streamUri(endpoints.mediaUrl, profile.token)Android-ലും iOS-ലും ONVIF ടു-വേ ഓഡിയോ
kotlin2rtc ഇതെല്ലാം ഇന്ന് Android-ലും ഏത് JVM-ലും ചെയ്യുന്നു: ക്യാമറ നൽകുന്ന ഏറ്റവും മികച്ച കോഡെക് (G.711, AAC-LC, 40 മുതൽ 16 kbit/s വരെ G.726, L16) തിരഞ്ഞെടുക്കുന്നു, സെഷൻ സജീവമായി നിലനിർത്തുന്നു, വൃത്തിയായി അടയ്ക്കുന്നു. Baby Zzz's Android ആപ്പിൽ IP ക്യാമറകളിലെ പുഷ് ടു ടോക്കും ഉറക്ക ശബ്ദങ്ങളും പ്രവർത്തിപ്പിക്കുന്നത് ഇതാണ്. iOS-ൽ, ബാക്ക്ചാനൽ swift2rtc-ക്കായി ആസൂത്രണം ചെയ്തിരിക്കുന്നു.
Kotlin-ൽ പുഷ് ടു ടോക്ക് (RTSP ബാക്ക്ചാനൽ)
import io.github.mafischer.kotlin2rtc.codec.BackchannelEncoder
import io.github.mafischer.kotlin2rtc.rtsp.RtspBackchannelClient
// Blocking calls: run them on an IO thread. The login rides in the URL (kept in memory only).
val client = RtspBackchannelClient(
url = "rtsp://camera-user:[email protected]:554/stream1",
allows = network::allowsCamera,
)
// DESCRIBE with "Require: www.onvif.org/ver20/backchannel"
val media = client.describe().media ?: return // no speaker kotlin2rtc can send to
val encoder = BackchannelEncoder(media) // G.711, G.726, AAC or L16, as the camera asked
encoder.microphoneRate(16_000)
val link = client.open() // SETUP (TCP interleaved) + PLAY
try {
while (talking) {
val block: FloatArray = readMicrophone(16_000 / 50) // one 20-millisecond block, -1..1
encoder.packets(sound = null, microphone = block).forEach(link::sendRtp)
}
} finally {
link.close() // TEARDOWN
}swift2rtc: iOS-നും macOS-നുമുള്ള ONVIF, RTSP
മൂന്ന് പ്രൊഡക്റ്റുകളുള്ള ഒരു Swift പാക്കേജ് (Swift 6, iOS 17, macOS 14):
ഓഡിയോ ബാക്ക്ചാനൽ (ടു-വേ ഓഡിയോ) ആസൂത്രണം ചെയ്തിട്ടുണ്ട്.
Swift Package Manager ഉപയോഗിച്ച് ഇൻസ്റ്റാൾ ചെയ്യുക
// Package.swift
dependencies: [
.package(url: "https://github.com/mafischer/swift2rtc.git", from: "0.1.1"),
],
targets: [
.target(name: "MyApp", dependencies: [
.product(name: "Swift2RTCOnvif", package: "swift2rtc"),
.product(name: "Swift2RTCRTSP", package: "swift2rtc"),
]),
]ഒരു ക്യാമറ കണ്ടെത്തി അതിന്റെ സ്ട്രീം നേടുക
import Swift2RTCOnvif
// Only private (local) addresses may be called
let isLocal: @Sendable (InetAddress) -> Bool = { $0.isPrivate }
let cameras = try await WSDiscovery(accept: isLocal).discover(timeout: 3)
let onvif = OnvifClient(
transport: SocketLanTransport(allows: isLocal),
deviceURL: cameras[0].deviceURL,
credentials: OnvifCredentials(username: "camera-user", password: "camera-password")
)
_ = try await onvif.syncClock()
let endpoints = try await onvif.endpoints()
let profiles = try await onvif.profiles(mediaURL: endpoints.mediaURL)
if let profile = profiles.first(where: { $0.hasVideo }) {
let rtspURL = try await onvif.streamURI(mediaURL: endpoints.mediaURL, profileToken: profile.token)
}RTP പാക്കറ്റുകളിൽ നിന്ന് ചിത്രങ്ങളിലേക്ക്
import Swift2RTCRTSP
let sdp = SDP.parse(describeAnswer) // the camera's DESCRIBE body
var video = VideoDepacketizer(codec: .h264) // or .h265
if let packet = RTPPacket.parse(interleavedPayload) {
for unit in video.push(packet) {
// unit.nalUnits: one picture, ready for a VideoToolbox decompression session
}
}Tapo ആപ്പ് ഇല്ലാതെ Tapo ടു-വേ ഓഡിയോ
Tapo C210 പോലുള്ള TP-Link Tapo ക്യാമറകൾ വീഡിയോയ്ക്ക് ONVIF, RTSP എന്നിവ നൽകുന്നു, പക്ഷേ അവയുടെ സ്പീക്കർ ONVIF ബാക്ക്ചാനൽ വഴി ശബ്ദം സ്വീകരിക്കുന്നില്ല: അതിനായി Tapo ആപ്പ് TP-Link-ന്റെ സ്വന്തം പ്രോട്ടോക്കോൾ ഉപയോഗിക്കുന്നു. Baby Zzz's ഇന്ന് ആ പ്രോട്ടോക്കോൾ സംസാരിക്കുന്നു: Android ആപ്പ് Tapo ക്യാമറയുടെ സ്പീക്കറിലൂടെ സംസാരിക്കുകയും ഉറക്ക ശബ്ദങ്ങൾ പ്ലേ ചെയ്യുകയും ചെയ്യുന്നു, ക്യാമറ കാണുന്ന ഓരോ രക്ഷിതാവിനും Baby Zzz's Desktop-ഉം അതുതന്നെ ചെയ്യുന്നു. ഇതിന് Tapo ആപ്പിൽ Third-Party Compatibility ഓണാക്കിയിരിക്കണം, കൂടാതെ TP-Link അക്കൗണ്ട് പാസ്വേഡും വേണം; അത് കുടുംബത്തിന്റെ ഉപകരണങ്ങൾ വിട്ടുപോകുന്നില്ല: അവ അതിന്റെ ഹാഷുകൾ മാത്രമേ സൂക്ഷിക്കൂ, എൻഡ്-ടു-എൻഡ് എൻക്രിപ്റ്റ് ചെയ്ത് പങ്കിടുന്നു.
സെർവറിന് പകരം ലൈബ്രറികൾ എന്തുകൊണ്ട്
go2rtc പോലുള്ള സെർവറുകൾ നിങ്ങൾ ഒരു ഹോം ലാബ് നടത്തുമ്പോൾ മികച്ചതാണ്: നിങ്ങളുടെ നെറ്റ്വർക്കിലെ ഒരു ബോക്സ് എല്ലാ ക്യാമറകളുമായും സംസാരിച്ച് അവയെ വീണ്ടും സ്ട്രീം ചെയ്യുന്നു. ഒരു ബേബി മോണിറ്റർ ആപ്പിന് വ്യത്യസ്ത ആവശ്യങ്ങളാണ്:
TelemetryKit: നേറ്റീവ് Android, iOS ആപ്പുകൾക്കായി സ്വകാര്യതയ്ക്ക് മുൻഗണന നൽകുന്ന Grafana Faro SDK
Baby Zzz's ഇത് എങ്ങനെ ഉപയോഗിക്കുന്നു
ഇൻസ്റ്റാൾ ചെയ്യുക (Maven Central, Swift Package Manager)
// Package.swift
dependencies: [
.package(url: "https://github.com/baby-zzz/telemetrykit-swift.git", from: "0.4.0"),
],
targets: [
.target(name: "MyApp", dependencies: [.product(name: "TelemetryKit", package: "telemetrykit-swift")]),
]// build.gradle.kts (Maven Central)
dependencies {
implementation("app.babyzzz.telemetrykit:telemetrykit:0.4.0")
}നിങ്ങളുടെ ആദ്യ ഇവന്റ് അയയ്ക്കുക (Swift)
import TelemetryKit
var configuration = TelemetryClient.Configuration(
endpoint: URL(string: "https://collector.example.com/collect")!, // your own Faro receiver, e.g. Grafana Alloy
app: FaroMeta.App(name: "my-app", namespace: "ios", version: "1.4.0+212", environment: "production",
bundleId: "com.example.myapp"),
browser: .describing(name: "MyApp-iOS", version: "1.4.0", osName: "iOS", language: "en", mobile: true),
eventDomain: "ios",
consentKey: "telemetry_consent",
debugOverrideKey: "MyTelemetry",
sessionKey: "telemetry_session",
release: true
)
configuration.consentDefault = true // the default is false: nothing is sent until the user agrees
Telemetry.start(configuration, storeURL: Telemetry.cachesFileURL("telemetry/pending.json"))
Telemetry.setRemoteEnabled(true) // your server's kill switch: nil = not known yet, false = drop everything
Telemetry.event("stream_started", ["quality": "hd"])
Telemetry.error(error, "alert") // type, kind and the message, with credentials and e-mail addresses removedനിങ്ങളുടെ ആദ്യ ഇവന്റ് അയയ്ക്കുക (Kotlin)
val configuration = Configuration(
endpoint = "https://collector.example.com/collect", // your own Faro receiver, e.g. Grafana Alloy
app = FaroMeta.App(name = "my-app", namespace = "android", version = "1.4.0+42",
environment = "production", bundleId = "com.example.app"),
browser = FaroMeta.Browser(name = "MyApp-Android", version = "1.4.0", os = "Android 16",
mobile = true, language = "en"),
platform = "android",
domain = "android",
consentKey = "telemetry_consent",
sessionKey = "telemetry_session",
)
Telemetry.start(configuration, store = MyStore(prefs), storeFile = File(cacheDir, "telemetry/pending.json"),
debug = BuildConfig.DEBUG, debugSwitch = false)
Telemetry.setRemoteEnabled(true)
Telemetry.event("checkout_started", mapOf("step" to "payment"))
Telemetry.error(exception, where = "checkout")ക്ലിക്ക്, ടാപ്പ്, ക്രാഷ് എന്നിവയുടെ സ്വയമേവയുള്ള ട്രാക്കിംഗ് (ഓപ്റ്റ്-ഇൻ, അടുത്ത റിലീസിൽ വരുന്നു)
കോർ മാനുവലായും അധിക ഡിപൻഡൻസികളില്ലാതെയും തുടരുന്നു. സ്വയമേവയുള്ള ഇൻസ്ട്രുമെന്റേഷൻ വേണ്ട ആപ്പുകൾക്ക്, ഒരു പ്രത്യേക കമ്പാനിയൻ പാക്കേജ് ഒറ്റ ഇൻസ്റ്റാൾ കോളിൽ അത് ചെയ്യുന്നു, അതിനാൽ ഓരോ ബട്ടണിലും സ്ക്രീനിലും ട്രാക്കിംഗ് കോഡ് വേണ്ട. ഇത് ഇതുവരെ റിലീസ് ചെയ്തിട്ടില്ല; അടുത്ത TelemetryKit റിലീസിനൊപ്പം വരും.
// build.gradle.kts: the opt-in companion (AAR, next TelemetryKit release; the core stays manual)
dependencies {
implementation("app.babyzzz.telemetrykit:telemetrykit-androidx:<next release>")
}
// Application.onCreate(), after Telemetry.start(...): lifecycle, crashes, exit reasons, screens, Compose taps
TelemetryAndroid.install(application)
// Dialogs and sheets are not part of the Activity's Compose content: track their taps with a modifier
Button(onClick = { /* ... */ }, modifier = Modifier.trackTaps(/* ... */)) { Text("Pay") }// Package.swift: the opt-in product of the same package (next TelemetryKit release)
.product(name: "TelemetryKitAutoInstrumentation", package: "telemetrykit-swift")
// After Telemetry.start(...): lifecycle, crashes, MetricKit exits and hangs, screens, UIKit taps
TelemetryAutoInstrumentation.install()
// SwiftUI: one line per tracked control (UIKit controls are tracked through their accessibility identifier)
Button("Pay") { /* ... */ }.telemetryTap("pay")കോഡ് സാമ്പിളുകൾ ഉദ്ദേശിക്കുന്ന രൂപം കാണിക്കുന്നു; റിലീസിന് മുമ്പ് കൃത്യമായ പേരുകൾ മാറിയേക്കാം.
TelemetryKit ഒരു സ്വതന്ത്ര പ്രോജക്റ്റാണ്, Grafana Labs-മായി ബന്ധപ്പെട്ടതോ അവർ അംഗീകരിച്ചതോ അല്ല.