Skip to content

Radio and Video apps (v0.7.0) - #1

Draft
storagebits wants to merge 7 commits into
mainfrom
feature/media
Draft

storagebits wants to merge 7 commits into
mainfrom
feature/media

Conversation

@storagebits

Copy link
Copy Markdown
Owner

Two media apps, and the first feature to follow the branch → pre-release → main workflow.

What's new

  • Radio: MP3 web radio on the speaker — Radio Paradise, BBC World Service, KEXP, FIP, Radio Swiss Classic, or a custom http:// stream. Song titles from ICY metadata, volume, keeps playing with the screen off.
  • Video: an MJPEG camera (ESP32-CAM, IP camera, 3D printer) or a JPEG snapshot URL, full screen, with a status overlay.
  • Shared, host-tested C pipelines (src/data/kg_media.c); minimp3 and TJpgDec vendored in src/third_party/ with their licences.
  • Release workflow: tags with a suffix (v0.7.0-beta.1) publish pre-releases.

Checked so far

  • Host tests: ICY, ring buffer, MP3 decode end to end, MJPEG extraction, JPEG colours.
  • Real streams from the simulator: the five stations decode without underruns (redirects included); 15 fps MJPEG from tools/mjpeg_test_server.sh.
  • Firmware builds without warnings; static RAM +1 KB (buffers in PSRAM, per session).

Before merging — on the watch

  • Radio plays on the speaker, no stutter, with Bluetooth on; titles shown; volume.
  • Radio keeps playing with the screen off; stops cleanly.
  • Video from an MJPEG source (e.g. tools/mjpeg_test_server.sh on a computer of the same network).
  • Internal RAM ≥ 30 % free while playing.

🤖 Generated with Claude Code

https://claude.ai/code/session_01BHBYox5ENEx9Hdd9LiBx8a

storagebits and others added 7 commits October 14, 2026 17:05
kg_http_request() generalises the toolbox's GET: http://host:port/path URLs, up to three
redirects (relative ones included), SHOUTcast's "ICY 200" status line, extra request
headers, response headers through a callback, an idle timeout for endless streams and an
optional total one. kg_http_get() is now a thin wrapper.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BHBYox5ENEx9Hdd9LiBx8a
kg_media: portable loops shared by the watch and the simulator.
- Radio: MP3 over HTTP into a single-producer / single-consumer ring buffer, ICY metadata
  stripped for the song title (UTF-8 or Latin-1), minimp3 decoding to mono with a
  quadratic volume; prebuffering, rebuffering after an underrun, reconnection.
- Video: JPEG images cut out of an MJPEG stream (or a snapshot URL polled every second),
  decoded by TJpgDec, scaled and centred into 240 × 240 RGB565, triple-buffered.
- Vendored in src/third_party with their licences: minimp3 (CC0; its 16 KB scratch can come
  from the caller, "Kronograf patch") and TJpgDec R0.03 (RGB565, 32-bit fast decode).
- Tests: ICY stripping across chunk boundaries, ring wrap-around, a 1 s 440 Hz MP3 decoded
  end to end (879 zero crossings for 880), two JPEGs extracted from a multipart stream and
  decoded with the right SMPTE colours. Fixtures generated with ffmpeg.
- tests/net_probe.c plays real stations and MJPEG streams without sound: the five
  built-in stations decode without underruns, redirects included.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BHBYox5ENEx9Hdd9LiBx8a
- Watch (media.cpp): radio in two tasks on core 0 (network, decoder) with buffers in
  PSRAM, Wi-Fi kept on while it plays (net_keep), screen off included; video in one task
  under the app's Wi-Fi lease. URLs and volume in NVS ("kgmedia").
- Speaker (feedback.cpp): a 16-bit mono stream at the station's rate; one user at a time,
  tones are dropped while the radio plays.
- Console: radio [play <n> | stop | vol <n> | url <u>], video [play | stop | url <u>].
- Simulator: demo data for screenshots (KG_RADIO, KG_VIDEO_URL, a test card decoded by
  TJpgDec); with KG_NET_REAL=1 the real loops, the radio through SDL audio.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BHBYox5ENEx9Hdd9LiBx8a
- Radio: station and genre, scrolling song title, stream state, previous / play-stop /
  next (switches station while playing), volume; long-press the station for a custom URL.
- Video: full-screen camera picture, a status overlay for the first seconds or on tap
  (frame rate, source size, bit rate), URL entry on the watch.
- The launcher's second page: Network, Radio, Video, Remote, System, Settings.
- README (features, console, simulator, development workflow), DECISIONS D57–D59, and the
  overview's last row: world clock, sun & moon, radio, video.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BHBYox5ENEx9Hdd9LiBx8a
A tag such as v0.7.0-beta.1 or v0.7.0-rc.1 is published as a GitHub pre-release with a
note that it is a testing build, never as "Latest". Plain vX.Y.Z tags are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BHBYox5ENEx9Hdd9LiBx8a
Measured on the watch with beta.1:
- The radio's network task peaked at 5.9 of its 6 KB of stack (DNS, redirects): it gets
  7 KB, and its 1.5 KB staging buffer moves into the session (PSRAM). The decoder used
  1.5 of 6 KB: 3.5 KB now. Six I²S DMA buffers instead of eight. The console's "radio"
  shows the stacks' free space while playing.
- Results: no underruns with Bluetooth on, ~24 % internal RAM free while playing (31 %
  once stopped); video at 5–6 fps, 27 % free.
- tools/mjpeg_test_server.py replaces the ffmpeg-only script, whose built-in server takes
  a single client and stayed stuck on a stale connection; it also serves /snapshot.jpg.
Version 0.7.0-beta.2.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BHBYox5ENEx9Hdd9LiBx8a
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant