1 Repository map & touch-points
| What you have | Where to extend / create | ||
|---|---|---|---|
app/src/main/java/com/urkob/wittrail_android/CameraXRecordingService.kt (Wittrail Gitea) |
Extend: add an EncoderPipeline inner object and a WebRtcStreamer helper. | ||
app/src/main/java/com/urkob/wittrail_android/MainActivity.kt (Wittrail Gitea) |
Minor: request the new foreground-service types at runtime (API 34). | ||
no data/ layer yet |
Add a repository package that holds an UploadRepository to push encrypted chunks via gRPC–WebRTC DTLS. |
||
AndroidManifest.xml (root) (Wittrail Gitea) |
Add <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION"/> (API 34+) and declare the service type `<foregroundServiceType camera |
microphone | dataSync>` (Android Developers) |
build.gradle (app) |
Add implementation("org.webrtc:google-webrtc:1.0.32006"), implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.1") and the CameraX BOM (androidx.camera:camera-bom:1.3.0) (WebRTC) |
2 Client-side capture → encoder → WebRTC pipeline
2.1 Capture & encode on-device
// inside CameraXRecordingService.kt (new)
private val encoderPipeline by lazy {
val video = MutableSharedFlow<EncodedVideoChunk>(replay = 0)
val audio = MutableSharedFlow<EncodedAudioChunk>(replay = 0)
// Register camera surface
VideoCapture.withOutput(MediaStoreOutputOptions.Builder(...).build())
.also { it.setVideoEncoder(VideoEncoderConfig.H264(2_000_000)) } // ≈2 Mbps
.also { it.setAudioEncoder(AudioEncoderConfig.AAC(128_000)) }
.registerPipeline(video, audio) // <-- extension function you’ll add
EncoderPipeline(video, audio) // data-class you create
}
Why H-264 + AAC? They are hardware-accelerated on every device since API 21 ✔️ and directly understood by org.webrtc’s MediaCodecVideoEncoder (GitHub).
CameraX’s VideoCapture already feeds a MediaCodec session internally (WebRTC), so you only tap its encoded output instead of raw YUV/PCM.
2.2 Secure streaming with WebRTC PeerConnection
- Create
WebRtcStreamer.ktthat owns a singlePeerConnection+ twoRtpSenders (audio/video). - Call
webRtcStreamer.sendVideo(chunk)each timeencoderPipeline.video.emit(chunk)fires. - Negotiate DTLS-SRTP with the server; the signalling channel can be a lightweight REST call to your back-end (later replaceable by gRPC bidirectional).
- Enable SFrame end-to-end media encryption if you need payload privacy; the hash for the timestamp is calculated server-side after DTLS decryption.
A bare-bones set-up (no ICE-servers, only TURN) is under 50 lines – see Google’s minimal Kotlin sample .
3 Server-side hash + qualified time-stamp
-
Deploy a tiny Go service that
- Receives SRTP packets from
webrtc::TrackLocalStaticRtp; - Re-orders & re-assembles them into MP4 fragments every N seconds (2–5 s gives smooth playback);
- Calculates a SHA-256 digest for each fragment and calls the Camerfirma Q-TSA REST endpoint (
/timestamp) – they follow ETSI EN 319-421 / 422 .
- Receives SRTP packets from
-
Store
{hash, RFC3161_tsToken, fragmentURI}in Postgres. Because the time-stamps are qualified, Art. 41.2 eIDAS les da “valor probatorio equivalente al de una firma notarial” in la UE.
4 Permissions & API-level quirks
| Feature | API ≤ 33 | API 34+ (Android 14) |
|---|---|---|
| Foreground-service | android.permission.FOREGROUND_SERVICE |
MUST also declare type in manifest and request FOREGROUND_SERVICE_MEDIA_PROJECTION at runtime (Android Developers) |
| Camera + Audio | CAMERA, RECORD_AUDIO (Android Developers) |
Igual, pero si target 34, the Privacy Sandbox dialog is automatic. |
| External write | Scoped-storage; use MediaStore (already in your code) |
Same; WRITE_EXTERNAL_STORAGE not needed ≥ API 30. |
5 Examples of good Issues for non-devs (compliance stream)
| Title | Description (plain language) | Labels | Milestone |
|---|---|---|---|
| “Review ETSI 319-421 compliance gap” | “Read the norm, tick each MUST & SHOULD in an excel, flag red items.” | legal, doc, research |
✍ ETSI analysis |
| “Choose EU funding window” | “Compare Digital Europe vs Horizon Europe: eligibility, calendar, TRL. Write 1-page summary.” | funding, planning |
💶 Grant Scan Q3 |
| “Draft DPIA (Data Protection Impact Assessment)” | “Fill the AEPD template for the pilot.” | privacy, regulation |
📜 Trust Ops MVP |
Your UX guide on how to create them—complete with screenshots—already lives in docs/onboarding/issues_guide.md; link to that in every new employee e-mail.
6 Pros / contras of this architecture
| Aspect | Pros | Contras / Mitigation |
|---|---|---|
| CameraX + MediaCodec | HW-accelerated, low battery, single API | Needs targetSdk 33+; some Samsung A-series ships buggy encoder → keep device-quirks table. |
| WebRTC SRTP path | No open port on device; NAT traversal built-in | TURN fees if many users → deploy coturn on cheap edge VMs. |
| Qualified TSA | Probative value by default (Art. 41 eIDAS) | Each token ≈ €0.05 at volume → batch 5 s fragments. |
| Hash first, TSA second | Small payload; privacy (only digest leaves device) | Requires buffering until TSA response (<300 ms). |
Next steps
- Add the new permissions to
AndroidManifest.xml. - Drop the code snippets above and run on API 33 for smoke-test.
- Spin up
pion/webrtcdemo server (one-liner) and verify you see live video. - Swap in Camerfirma sandbox credentials and log the RFC-3161 token.
Once that works end-to-end, you can harden (device attestation, FIDO key-sealed AES, etc.) and then move to the larger roadmap we drafted earlier