Cryptograss:Video encoding pipeline: Difference between revisions

From PickiPedia: A knowledge base of bluegrass, old time psychedelic jams, and other public domain music
Jump to navigationJump to search
Magent (talk | contribs)
Document how video gets from upload to Release page, and the September 2026 rework that moved encoding off Coconut and onto delivery-kid (via create-page on MediaWiki MCP Server)
 
Magent (talk | contribs)
Update for the quality ladder, poster frames, upright video, measured encode times; refresh known issues and releases
Line 5: Line 5:
# '''Upload.''' [[Special:DeliverVideo]] mints a draft, then the browser posts the video bytes '''directly to delivery-kid''' — they never pass through the wiki server. A <code>ReleaseDraft:</code> page is created first, so an upload that fails leaves an inspectable record instead of vanishing.
# '''Upload.''' [[Special:DeliverVideo]] mints a draft, then the browser posts the video bytes '''directly to delivery-kid''' — they never pass through the wiki server. A <code>ReleaseDraft:</code> page is created first, so an upload that fails leaves an inspectable record instead of vanishing.
# '''Analyse.''' delivery-kid probes the file for duration, resolution and codecs, and writes them onto the draft page.
# '''Analyse.''' delivery-kid probes the file for duration, resolution and codecs, and writes them onto the draft page.
# '''Finalize.''' The video is transcoded to [[wikipedia:AV1|AV1]] video and [[wikipedia:Opus_(audio_format)|Opus]] audio, packaged as HLS, and pinned to IPFS.
# '''Finalize.''' The video is transcoded to [[wikipedia:AV1|AV1]] video and [[wikipedia:Opus_(audio_format)|Opus]] audio in '''four renditions''' (see [[#The quality ladder|the quality ladder]]), a poster frame is extracted, and the whole directory is pinned to IPFS under one CID.
# '''Publish.''' A bot notices the finalized draft, creates the <code>Release:</code> page, and adds BitTorrent metadata so the release seeds as well as pins.
# '''Publish.''' A bot notices the finalized draft, creates the <code>Release:</code> page, and adds BitTorrent metadata so the release seeds as well as pins.


Steps 3 and 4 are automatic and typically complete within seconds of each other.
Encoding is the slow step — budget '''about two and a half times the video's length''' — and everything after it takes seconds. Progress is reported live on the draft page throughout, and a snapshot of the whole log is written to <code>ReleaseDraft:&lt;uuid&gt;/diagnostics</code> at every terminal state, so the record survives even if delivery-kid's storage is rebuilt.


=== Why AV1 and Opus ===
=== Why AV1 and Opus ===
Line 14: Line 14:
Both are '''royalty-free'''. H.264 and AAC — what cameras and phones actually record — are covered by patent pools. Since cryptograss exists to distribute music freely and durably, the published artefact should not depend on a licensing regime that can change. Encoding is also '''DRM-free''': nothing here is encrypted or access-controlled, and the file is downloadable from IPFS by anyone.
Both are '''royalty-free'''. H.264 and AAC — what cameras and phones actually record — are covered by patent pools. Since cryptograss exists to distribute music freely and durably, the published artefact should not depend on a licensing regime that can change. Encoding is also '''DRM-free''': nothing here is encrypted or access-controlled, and the file is downloadable from IPFS by anyone.


The practical cost is that '''Apple ships no software AV1 decoder'''. Safari plays AV1 only on M3-and-later Macs and A17 Pro-and-later iPhones. Chrome and Firefox both bundle the royalty-free <code>dav1d</code> decoder and play AV1 on any desktop, including older Intel Macs. So on Apple hardware without AV1 support, '''use Firefox or Chrome''', or download the file and play it in VLC.
The practical cost is that '''Apple ships no software AV1 decoder'''. Safari plays AV1 only on M3-and-later Macs and A17 Pro-and-later iPhones. Chrome and Firefox both bundle the royalty-free <code>dav1d</code> decoder and play AV1 on any desktop, including older Intel Macs. So on Apple hardware without AV1 support, '''use Firefox or Chrome''', or download the file and play it in VLC. The player says as much when it cannot decode, rather than blaming IPFS.
 
== The quality ladder ==
 
Every video is published as four streams under a single CID, listed in <code>master.m3u8</code>. The player picks one by bandwidth and switches as conditions change; a listener can also choose the audio-only stream deliberately.
 
{| class="wikitable"
! Stream !! Short side !! CRF !! Audio !! Typical bitrate
|-
| <code>stream_1080p</code> || 1080 px || 32 || 128 kbps || 2.6–5 Mbps
|-
| <code>stream_720p</code> || 720 px || 34 || 128 kbps || 1.6–2.4 Mbps
|-
| <code>stream_360p</code> || 360 px || 36 || 96 kbps || 0.65–1.0 Mbps
|-
| <code>stream_audio</code> || — || — || 160 kbps || ~0.15 Mbps
|}
 
The '''audio-only''' stream is roughly fifty times smaller than 1080p, which makes it the one certain to work from a campground, and for an archive of music it is often the one people actually want. It gets the better audio bitrate for that reason.
 
Rungs larger than the source are skipped rather than upscaled. '''A rung is named for the short side of the picture''', so an upright phone video is "1080p" at 1080×1920 exactly as a landscape one is at 1920×1080. Sizing by the tall side instead produced a 608×1080 "1080p" from a 1080×1920 phone clip, discarding well over half the picture. ''([https://github.com/cryptograss/maybelle-config/pull/124 maybelle-config#124], [https://github.com/cryptograss/maybelle-config/pull/128 #128])''
 
A '''poster frame''' (<code>poster.jpg</code>) is written alongside, taken about ten percent in so it does not land on a lens cap or an empty stage. It is a JPEG, so it renders even where the AV1 behind it will not — a Safari viewer at least sees what is on offer. ''([https://github.com/cryptograss/maybelle-config/pull/120 maybelle-config#120])''
 
[https://github.com/cryptograss/maybelle-config/pull/125 <code>scripts/reladder.py</code>] rebuilds an already-published release as a ladder. It copies the existing top rendition and the audio bit-for-bit and only encodes the smaller rungs, so the original quality is never re-compressed. It cannot recover detail that was never published: a release whose best stream is 608×1080 stays that size, and only a re-upload of the original file restores it.


== The September 2026 rework ==
== The September 2026 rework ==
Line 33: Line 57:
; delivery-kid could never write to the wiki
; delivery-kid could never write to the wiki
: The wiki client was built with mwclient's default path of <code>/w/</code>, but PickiPedia serves its API at <code>/api.php</code>. Every write 404'd, so every diagnostics snapshot silently no-opped, and draft pages froze at whatever the browser last wrote.
: The wiki client was built with mwclient's default path of <code>/w/</code>, but PickiPedia serves its API at <code>/api.php</code>. Every write 404'd, so every diagnostics snapshot silently no-opped, and draft pages froze at whatever the browser last wrote.
; A successful publish could still report total failure
: The CID was held only in the browser's event stream. If anything after the pin failed, the source was deleted as "safely published" while nothing recorded where it went. The publish is now written down three ways — the draft file, a <code>finalized/</code> ledger, and the wiki snapshot — before the uploader is told anything. ''([https://github.com/cryptograss/maybelle-config/pull/117 maybelle-config#117])''


=== What changed ===
=== What changed ===
Line 52: Line 79:
SVT-AV1 at preset 10 is '''faster than the patented libx264 profile it replaced'''. Coconut is now opt-in only; <code>auto</code> means local.
SVT-AV1 at preset 10 is '''faster than the patented libx264 profile it replaced'''. Coconut is now opt-in only; <code>auto</code> means local.


A detail worth recording, because it fails silently: '''AV1 and Opus cannot be carried in MPEG-TS segments'''. ffmpeg accepts such a command, exits zero, and writes a playlist whose streams probe back as <code>bin_data</code> — unplayable. HLS output here therefore uses fMP4/CMAF segments, with the GOP derived from source frame rate so keyframes land on segment boundaries. ''([https://github.com/cryptograss/maybelle-config/pull/113 maybelle-config#113])''
Encoding four renditions instead of one costs roughly 2.5× the video's length, measured end to end on real uploads:
 
{| class="wikitable"
! Upload !! Output !! Speed
|-
| 27&nbsp;s landscape, single 1080p rendition || 1920×1080 || 1.13× realtime
|-
| 24&nbsp;s upright, full ladder || 1080×1920 top rung || 0.43× realtime
|}


Finally, encoding an hour-long video locally meant the finalize page sat frozen for the duration. ffmpeg's progress is now streamed to the page, which reports position, encoding speed and a running estimate:
So a 66-minute set is '''around two and a half hours''' of encoding. The page reports position, speed and a running estimate throughout, so a long encode looks busy rather than hung:


<pre>Encoding 34% · 11m40s of 34m00s · 1.36x · ~16m left</pre>
<pre>Encoding 34% · 11m40s of 34m00s · 1.36x · ~16m left</pre>


''([https://github.com/cryptograss/maybelle-config/pull/114 maybelle-config#114] and [https://github.com/cryptograss/pickipedia/pull/108 pickipedia#108])''
''([https://github.com/cryptograss/maybelle-config/pull/114 maybelle-config#114] and [https://github.com/cryptograss/pickipedia/pull/108 pickipedia#108])''
A detail worth recording, because it fails silently: '''AV1 and Opus cannot be carried in MPEG-TS segments'''. ffmpeg accepts such a command, exits zero, and writes a playlist whose streams probe back as <code>bin_data</code> — unplayable. HLS output here therefore uses fMP4/CMAF segments, with the GOP derived from source frame rate so keyframes land on segment boundaries. ''([https://github.com/cryptograss/maybelle-config/pull/113 maybelle-config#113])''


== Known issues ==
== Known issues ==


* '''[https://github.com/cryptograss/maybelle-config/issues/115 maybelle-config#115]''' — A finalize that pins successfully but fails afterwards deletes the source and leaves no record, so it reports as total failure when the content is in fact published. Encountered on the 44th Bday Cicada Jam-Bash, which had been pinned and seeding for hours while the wiki showed nothing and the audit recommended deleting the page. Recovered by writing the CID back onto the draft.
* '''[https://github.com/cryptograss/maybelle-config/issues/126 maybelle-config#126]''' — the '''preview''' shown on the draft page before publishing is still sent to Coconut, and its result has never been used: the job asks for an output named <code>mp4</code> while the handler looks for <code>mp4_preview</code>, so the page falls back to playing the original upload. Each one spends a Coconut credit, waits about 30 seconds, and pins a one-file directory that nothing references. Awaiting a decision on removal.
* '''[https://github.com/cryptograss/maybelle-config/issues/110 maybelle-config#110]''' — the original Coconut failure. Largely moot now that Coconut is out of the default path.
* '''[https://github.com/cryptograss/maybelle-config/issues/121 maybelle-config#121]''' — four tests in the Coconut suite assert an output schema the code no longer emits. They are excluded from CI, loudly, until Coconut's fate is settled.
* Diagnostics snapshots to <code>ReleaseDraft:&lt;uuid&gt;/diagnostics</code> have never successfully written a page. The <code>/w/api.php</code> path bug was one cause; a second remains, most likely an unset bot password in the container.
* '''[https://github.com/cryptograss/maybelle-config/issues/110 maybelle-config#110]''' — the original Coconut failure on very large uploads. Now confined to the preview path, and resolved by #126 either way.
 
Fixed since this page was first written: the lost-publish bug (#115), the diagnostics pages that had never been written, and every block height on the wiki being about ten days in the future.


== First releases through the local pipeline ==
== Releases through the local pipeline ==


{| class="wikitable"
{| class="wikitable"
! Release !! Length !! Finalized
! Release !! Length !! Finalized !! Note
|-
| [[Release:QmZCHD2Jcq1wCuiSWGrGNg8TeE37XVCijqNkQ59jyyqmW5|Pickipedia Dispatch — Sombrero Edition]] || 34 min || 2 September 2026 || first local encode
|-
| [[Release:QmbrE35neh3CbRRZZMPXBwUrqsM6M3M8veEWSkD1pm9BKR|44th Bday Cicada Jam-Bash]] || 66 min || 3 September 2026 || recovered after the lost-publish bug
|-
| [[Release:QmToKY4gKxsRmA4ZY4ynrU8s5keH6PUVWHWSTjMTU7cwyB|Cicada 44th Bday Jam Set 1]] || 13 min || 4 September 2026 || first with no manual intervention
|-
|-
| [[Release:QmZCHD2Jcq1wCuiSWGrGNg8TeE37XVCijqNkQ59jyyqmW5|Pickipedia Dispatch — Sombrero Edition]] || 34 min || 2 September 2026
| [[Release:QmPXTaQcJnM9gSofDKBFR6DUn7MgXiXk5roJfXsdQjauX5|Skyler Serenading Strangers on the Shore]] || 27 s || 14 September 2026 || first diagnostics page ever written
|-
|-
| [[Release:QmbrE35neh3CbRRZZMPXBwUrqsM6M3M8veEWSkD1pm9BKR|44th Bday Cicada Jam-Bash]] || 66 min || 3 September 2026
| [[Release:QmdgxqVbPpLu1Rp8Sn46BqQzRbbpnwrYG3X2FTFfUVGDvQ|Iceland Hot Springs Flute with Justin Holmes]] || 24 s || 15 September 2026 || first quality ladder; upright, so published at 608×1080
|-
|-
| [[Release:QmToKY4gKxsRmA4ZY4ynrU8s5keH6PUVWHWSTjMTU7cwyB|Cicada 44th Bday Jam Set 1]] || 13 min || 4 September 2026
| [[Release:QmSAeiTZ2gMUYPfF9p8kj5ApuKohSgEru3RzRBBc82o8xS|Some band practice... in a field somewhere]] || 38 s || 17 September 2026 || first upright video at full 1080×1920
|}
|}


All three are AV1 1080p with Opus audio. The third was the first to run start to finish with no manual intervention.
The first three are single-rendition 1080p and predate the ladder.


== Advice for uploaders ==
== Advice for uploaders ==
Line 84: Line 129:
* '''Export around 8–12 Mbps for 1080p.''' Camera and editor defaults are often 21 Mbps or higher, which is far more than jam footage needs — that is the difference between a 5&nbsp;GB upload and a 10.5&nbsp;GB one, with no visible gain.
* '''Export around 8–12 Mbps for 1080p.''' Camera and editor defaults are often 21 Mbps or higher, which is far more than jam footage needs — that is the difference between a 5&nbsp;GB upload and a 10.5&nbsp;GB one, with no visible gain.
* '''Do not economise on audio.''' 256–320&nbsp;kbps. It is a rounding error in file size and it is the part anyone actually listens to.
* '''Do not economise on audio.''' 256–320&nbsp;kbps. It is a rounding error in file size and it is the part anyone actually listens to.
* '''Upright phone video is fine.''' It is published at its full resolution. Send the file straight off the phone; re-exporting can bake in the rotation.
* '''Expect the encode to take about 2.5× the video's length''', and leave the page open — or don't, since the publish is recorded server-side either way.
* '''Fill in venue and performers before finalizing.''' Those fields become the Release page, and the performer names are what connect a video to everyone's pages later.
* '''Fill in venue and performers before finalizing.''' Those fields become the Release page, and the performer names are what connect a video to everyone's pages later.
* '''Storage is roughly the size of the source''' now that four renditions are kept. A 38&nbsp;s phone clip of 38&nbsp;MB published as 39.8&nbsp;MB across all four streams.


== See also ==
== See also ==
Line 91: Line 139:
* [[Special:DeliverVideo]] — the upload form
* [[Special:DeliverVideo]] — the upload form
* [[Special:Releases]] — everything published
* [[Special:Releases]] — everything published
* [[Cryptograss:On The Pipe]] — the failure pattern most of these bugs shared

Revision as of 13:36, 17 September 2026

Video encoding pipeline describes how a video travels from a musician's laptop to a published Release on PickiPedia — and, as of September 2026, why none of that work leaves delivery-kid any more.

How it works

  1. Upload. Special:DeliverVideo mints a draft, then the browser posts the video bytes directly to delivery-kid — they never pass through the wiki server. A ReleaseDraft: page is created first, so an upload that fails leaves an inspectable record instead of vanishing.
  2. Analyse. delivery-kid probes the file for duration, resolution and codecs, and writes them onto the draft page.
  3. Finalize. The video is transcoded to AV1 video and Opus audio in four renditions (see the quality ladder), a poster frame is extracted, and the whole directory is pinned to IPFS under one CID.
  4. Publish. A bot notices the finalized draft, creates the Release: page, and adds BitTorrent metadata so the release seeds as well as pins.

Encoding is the slow step — budget about two and a half times the video's length — and everything after it takes seconds. Progress is reported live on the draft page throughout, and a snapshot of the whole log is written to ReleaseDraft:<uuid>/diagnostics at every terminal state, so the record survives even if delivery-kid's storage is rebuilt.

Why AV1 and Opus

Both are royalty-free. H.264 and AAC — what cameras and phones actually record — are covered by patent pools. Since cryptograss exists to distribute music freely and durably, the published artefact should not depend on a licensing regime that can change. Encoding is also DRM-free: nothing here is encrypted or access-controlled, and the file is downloadable from IPFS by anyone.

The practical cost is that Apple ships no software AV1 decoder. Safari plays AV1 only on M3-and-later Macs and A17 Pro-and-later iPhones. Chrome and Firefox both bundle the royalty-free dav1d decoder and play AV1 on any desktop, including older Intel Macs. So on Apple hardware without AV1 support, use Firefox or Chrome, or download the file and play it in VLC. The player says as much when it cannot decode, rather than blaming IPFS.

The quality ladder

Every video is published as four streams under a single CID, listed in master.m3u8. The player picks one by bandwidth and switches as conditions change; a listener can also choose the audio-only stream deliberately.

Stream Short side CRF Audio Typical bitrate
stream_1080p 1080 px 32 128 kbps 2.6–5 Mbps
stream_720p 720 px 34 128 kbps 1.6–2.4 Mbps
stream_360p 360 px 36 96 kbps 0.65–1.0 Mbps
stream_audio — — 160 kbps ~0.15 Mbps

The audio-only stream is roughly fifty times smaller than 1080p, which makes it the one certain to work from a campground, and for an archive of music it is often the one people actually want. It gets the better audio bitrate for that reason.

Rungs larger than the source are skipped rather than upscaled. A rung is named for the short side of the picture, so an upright phone video is "1080p" at 1080×1920 exactly as a landscape one is at 1920×1080. Sizing by the tall side instead produced a 608×1080 "1080p" from a 1080×1920 phone clip, discarding well over half the picture. (maybelle-config#124, #128)

A poster frame (poster.jpg) is written alongside, taken about ten percent in so it does not land on a lens cap or an empty stage. It is a JPEG, so it renders even where the AV1 behind it will not — a Safari viewer at least sees what is on offer. (maybelle-config#120)

scripts/reladder.py rebuilds an already-published release as a ladder. It copies the existing top rendition and the audio bit-for-bit and only encodes the smaller rungs, so the original quality is never re-compressed. It cannot recover detail that was never published: a release whose best stream is 608×1080 stays that size, and only a re-upload of the original file restores it.

The September 2026 rework

Until early September 2026, transcoding was sent to Coconut, a paid cloud service. Long videos failed there consistently, and a chain of separate bugs made those failures nearly impossible to read. All of it surfaced from a single complaint: longer videos would not upload.

What was wrong

Uploads died instantly on large files
The upload handler read each file into memory in one allocation. On delivery-kid's 4 GB box a multi-gigabyte video exceeded available memory and raised MemoryError before copying a single byte — 13 milliseconds after the upload began. Because MemoryError stringifies to the empty string, the error surfaced as a bare "Upload error:" with nothing after it. Fixed by streaming to disk in 8 MB chunks: peak memory for a 200 MB file fell from 210 MB to 17 MB. (maybelle-config#108)
Coconut could not handle long videos
Two independent attempts on a 10.5 GB source failed 12m11s and 11m54s after submission — a fixed timeout, not a transient fault. Coconut's failure webhook carried no error field at all, so both the draft page and the container log reported only "Unknown error". (maybelle-config#110)
The local fallback used patented codecs
The "fallback" path was libx264 + aac — quietly undermining the royalty-free goal that sent the work to Coconut in the first place. It also could not be reached: once Coconut accepted a job, the finalize handler returned, and the job's later failure arrived by webhook with nothing left to fall back to.
delivery-kid could never write to the wiki
The wiki client was built with mwclient's default path of /w/, but PickiPedia serves its API at /api.php. Every write 404'd, so every diagnostics snapshot silently no-opped, and draft pages froze at whatever the browser last wrote.
A successful publish could still report total failure
The CID was held only in the browser's event stream. If anything after the pin failed, the source was deleted as "safely published" while nothing recorded where it went. The publish is now written down three ways — the draft file, a finalized/ ledger, and the wiki snapshot — before the uploader is told anything. (maybelle-config#117)

What changed

Encoding moved onto delivery-kid using SVT-AV1, which has become fast enough to make the cloud service unnecessary. Measured on two threads at 1080p, extrapolated to a 66-minute source:

Encoder Royalty-free Speed 66-minute source
SVT-AV1 preset 12 yes 0.51× realtime ~34 min
SVT-AV1 preset 10 yes 0.81× realtime ~53 min
libx264 medium no 1.32× realtime ~1.5 h
libvpx-VP9 speed 4 yes 2.69× realtime ~3.0 h

SVT-AV1 at preset 10 is faster than the patented libx264 profile it replaced. Coconut is now opt-in only; auto means local.

Encoding four renditions instead of one costs roughly 2.5× the video's length, measured end to end on real uploads:

Upload Output Speed
27 s landscape, single 1080p rendition 1920×1080 1.13× realtime
24 s upright, full ladder 1080×1920 top rung 0.43× realtime

So a 66-minute set is around two and a half hours of encoding. The page reports position, speed and a running estimate throughout, so a long encode looks busy rather than hung:

Encoding 34% · 11m40s of 34m00s · 1.36x · ~16m left

(maybelle-config#114 and pickipedia#108)

A detail worth recording, because it fails silently: AV1 and Opus cannot be carried in MPEG-TS segments. ffmpeg accepts such a command, exits zero, and writes a playlist whose streams probe back as bin_data — unplayable. HLS output here therefore uses fMP4/CMAF segments, with the GOP derived from source frame rate so keyframes land on segment boundaries. (maybelle-config#113)

Known issues

  • maybelle-config#126 — the preview shown on the draft page before publishing is still sent to Coconut, and its result has never been used: the job asks for an output named mp4 while the handler looks for mp4_preview, so the page falls back to playing the original upload. Each one spends a Coconut credit, waits about 30 seconds, and pins a one-file directory that nothing references. Awaiting a decision on removal.
  • maybelle-config#121 — four tests in the Coconut suite assert an output schema the code no longer emits. They are excluded from CI, loudly, until Coconut's fate is settled.
  • maybelle-config#110 — the original Coconut failure on very large uploads. Now confined to the preview path, and resolved by #126 either way.

Fixed since this page was first written: the lost-publish bug (#115), the diagnostics pages that had never been written, and every block height on the wiki being about ten days in the future.

Releases through the local pipeline

Release Length Finalized Note
Pickipedia Dispatch — Sombrero Edition 34 min 2 September 2026 first local encode
44th Bday Cicada Jam-Bash 66 min 3 September 2026 recovered after the lost-publish bug
Cicada 44th Bday Jam Set 1 13 min 4 September 2026 first with no manual intervention
Skyler Serenading Strangers on the Shore 27 s 14 September 2026 first diagnostics page ever written
Iceland Hot Springs Flute with Justin Holmes 24 s 15 September 2026 first quality ladder; upright, so published at 608×1080
Some band practice... in a field somewhere 38 s 17 September 2026 first upright video at full 1080×1920

The first three are single-rendition 1080p and predate the ladder.

Advice for uploaders

  • Export around 8–12 Mbps for 1080p. Camera and editor defaults are often 21 Mbps or higher, which is far more than jam footage needs — that is the difference between a 5 GB upload and a 10.5 GB one, with no visible gain.
  • Do not economise on audio. 256–320 kbps. It is a rounding error in file size and it is the part anyone actually listens to.
  • Upright phone video is fine. It is published at its full resolution. Send the file straight off the phone; re-exporting can bake in the rotation.
  • Expect the encode to take about 2.5× the video's length, and leave the page open — or don't, since the publish is recorded server-side either way.
  • Fill in venue and performers before finalizing. Those fields become the Release page, and the performer names are what connect a video to everyone's pages later.
  • Storage is roughly the size of the source now that four renditions are kept. A 38 s phone clip of 38 MB published as 39.8 MB across all four streams.

See also