Nimbin[12]?Web & Tools / Browser / commits / 51b041b

Browser git · main

Minimal browser in C (mini and big variant)

browser c · first commit 2026-08-25 · last commit 2026-10-29 (4 days ago) · synced 6 hours ago · upstream: github.com/nimbin2/Browser

C 96.1% Markdown 3.7%
git clone https://git.christianimmanuel.de/web-tools/Browser.gitwget https://git.christianimmanuel.de/web-tools/Browser/archive/Browser.tar.gz

Add streaming debug option

Christian Immanuel · 2026-10-29 18:50 · 51b041b90322c1598ceedc4e7a4eee26cba9cd3c

 Makefile      |  2 +-
 README.md     | 18 ++++++++++++++++--
 browser-big.c | 34 ++++++++++++++++++++++++++++------
 3 files changed, 45 insertions(+), 9 deletions(-)

diff --git a/Makefile b/Makefile
index 674ab6a..d48c593 100644
--- a/Makefile
+++ b/Makefile
@@ -2,7 +2,7 @@ CC      ?= gcc
 CFLAGS  ?= -O2 -Wall -Wextra -rdynamic
 PREFIX  ?= /usr
 
-VERSION  := 4.39.0
+VERSION  := 4.40.0
 SOURCES  := browser_core.c browser_core.h browser-mini.c browser-big.c Makefile
 BUILD_ID := $(shell cat $(SOURCES) 2>/dev/null | md5sum | cut -c1-7)
 
diff --git a/README.md b/README.md
index 659cc7e..6328076 100644
--- a/README.md
+++ b/README.md
@@ -30,7 +30,7 @@ make                # build both
 make install        # into /usr/bin (PREFIX=... to change)
 make uninstall
 make clean
-make version        # 4.39.0 (build …), md5 of the sources
+make version        # 4.40.0 (build …), md5 of the sources
 ```
 
 ## Usage
@@ -178,7 +178,8 @@ browser-big URL --rtc-trace 2>&1 | tee /tmp/bb.log
 | `sdp` | Each description, codecs as `payload-type name` |
 | `ssrc-real`, `ssrc-ws`, `pt-fixed` | The server got the real SSRC and payload type |
 | `rtc-stats` | Every 3 s: `out-*` sent, `far-*` server report, `in-*` received |
-| `video-state` | Each `<video>`: size, frames shown |
+| `video-state` | Each `<video>`: size, `frames` shown, `dframes` since last tick |
+| `stuck: true` | On `rtc-stats` or `video-state`: counters stopped moving |
 | `video-audio-split` | A silent-audio stream shown video-only |
 | `codec-mismatch` | We encode something the far end did not agree to |
 
@@ -186,6 +187,19 @@ browser-big URL --rtc-trace 2>&1 | tee /tmp/bb.log
 - `in-video.dec` climbing, `video-state.frames` at 0: decoded, never shown.
 - `in-video.fir` climbing, `keys` still: no keyframe arrives.
 
+**A frozen picture, audio still playing.** Repeated samples are not
+printed, so a freeze stops the log instead of filling it. One line is
+let through when the counters stop and one when they move again:
+
+- `video-state` `stuck: true`, `dframes: 0`, `in-video.dec` climbing:
+  frames decode, the element never presents them. Our bug.
+- `in-video.bytes` climbing, `dec` flat: packets arrive, the decoder
+  does not advance. Usually codec or keyframe.
+- `rtc-stats` `stuck: true`: nothing arrives any more. The page or the
+  server stopped sending.
+
+The timestamp on the `stuck` line is when the picture froze.
+
 **Without a server:**
 `browser-big https://webrtc.github.io/samples/src/content/peerconnection/pc1/ --rtc-trace`.
 If that works, WebKit's WebRTC is fine and the problem is site-specific.
diff --git a/browser-big.c b/browser-big.c
index 937e034..6e5c342 100644
--- a/browser-big.c
+++ b/browser-big.c
@@ -791,9 +791,21 @@ static const char *SHIM_JS =
 "        box:Math.round(r.width)+'x'+Math.round(r.height),"
 "        shown:st.display!=='none'&&st.visibility!=='hidden'&&+st.opacity>0,"
 "        track:vt.length?(vt[0].id.slice(0,8)+' '+vt[0].readyState+(vt[0].muted?'/muted':'')):'none'};"
-"      var k=JSON.stringify(o);"
-"      if(vlast.get(v)===k)continue;"
-"      vlast.set(v,k);post(o);"
+/*
+ * The repeat filter is what makes a freeze invisible: when the picture
+ * stops, every counter stops with it, this sample is identical to the
+ * last one and nothing is printed at all - so silence means either
+ * frozen or page gone, and the two cannot be told apart. Compare the
+ * frame counter on its own and let one line through on the transition
+ * into stuck and one on the way out. A frozen element then says so
+ * once, with dframes:0, instead of going quiet.
+ */
+"      var k=JSON.stringify(o),p=vlast.get(v)||{};"
+"      o.dframes=(o.frames>=0&&p.frames>=0)?o.frames-p.frames:-1;"
+"      o.stuck=(o.dframes===0&&o.ready>=2&&!o.paused);"
+"      var rep=(k!==p.k)||(!!o.stuck!==!!p.stuck);"
+"      vlast.set(v,{k:k,frames:o.frames,stuck:o.stuck});"
+"      if(rep)post(o);"
 "    }"
 "  },3000);"
 "  try{var SD=Object.getOwnPropertyDescriptor(HTMLMediaElement.prototype,'srcObject');"
@@ -859,9 +871,19 @@ static const char *SHIM_JS =
 "          o['rtrack-'+rv.track.kind]={id:rv.track.id.slice(0,8),state:rv.track.readyState,muted:!!rv.track.muted};"
 "        });}catch(e){}"
 "        if(!any)return;"
-"        var key=JSON.stringify(o);"
-"        if(key===last[i])return;"          /* nothing moved, do not repeat */
-"        last[i]=key;post(o);"
+/*
+ * Same on the receive side. Hold the three counters that have to move
+ * while video is arriving and being decoded, and report the tick they
+ * stop on: bytes still climbing with dec flat is "arriving, not
+ * decoding"; both flat is "nothing arriving". Without this the stats
+ * simply stop printing at the moment they get interesting.
+ */
+"        var key=JSON.stringify(o),p=last[i]||{},iv=o['in-video'];"
+"        var rx=iv?(iv.bytes+'/'+iv.pkts+'/'+iv.dec):null;"
+"        o.stuck=(rx!==null&&rx===p.rx);"
+"        var rep=(key!==p.key)||(!!o.stuck!==!!p.stuck);"
+"        last[i]={key:key,rx:rx,stuck:o.stuck};"
+"        if(rep)post(o);"
 "      }).catch(function(e){"
 "        if(!pc.__bbStatsErr){pc.__bbStatsErr=1;"
 "          post({ev:'rtc-stats-error',pc:i,message:String((e&&e.message)||e)});}"