diff --git a/examples/README.md b/examples/README.md index e3aef43..9a2ee5d 100644 --- a/examples/README.md +++ b/examples/README.md @@ -23,6 +23,11 @@ Note that some starter examples include the creation of a `brightsign-dumps` fol - **Location**: `examples/local-storage` - **Features**: Image slideshow that caches images in browser localStorage for persistent, smooth playback and looping. +#### Large File Download Example + +- **Location**: `examples/large-file-download` +- **Features**: Downloads large files (multi-GB) to SD card on memory-constrained players without OOM or UI blocking. Uses Node.js streams with TCP-level backpressure via `roHtmlWidget`. + ### Node.js Examples #### Node Starter Example diff --git a/examples/large-file-download/README.md b/examples/large-file-download/README.md new file mode 100644 index 0000000..37a5afb --- /dev/null +++ b/examples/large-file-download/README.md @@ -0,0 +1,57 @@ +# Large File Download Example + +## Introduction + +This example demonstrates how to download a large file (multi-GB) to an SD card on a memory-constrained BrightSign player without running out of memory or blocking the UI. + +The application is defined in `index.html` which uses Node.js streams via `roHtmlWidget` to download a file with true TCP-level backpressure. There is an `autorun.brs` file which tells the player to run the application. + +## Why Not Use `fetch()`? + +The browser's Fetch API does not propagate backpressure to the network layer. When downloading from a fast network connection to a slow SD card, `fetch()` will buffer the entire response body in memory — causing an OOM kill on devices with limited RAM (500 MB or less). + +This example uses Node's `http`/`https` modules with `stream.pipeline()` instead. When the SD card's write buffer is full, backpressure propagates all the way back to the TCP socket, causing the sender to slow down. Memory usage stays bounded to roughly 48 KB (three stream buffers at 16 KB each) regardless of file size. + +## How It Works + +1. **Node.js HTTP request**: Uses `http.get()` / `https.get()` to initiate the download, which returns a native Node.js readable stream +2. **Metering Transform stream**: A passthrough `Transform` stream counts downloaded bytes for progress reporting +3. **`stream.pipeline()`**: Wires the response stream → meter → file write stream with automatic backpressure and error teardown +4. **Decoupled UI rendering**: A `requestAnimationFrame` loop updates progress, a CSS progress bar, and an FPS counter independently of the download — the main thread is never blocked +5. **Redirect handling**: Follows HTTP 3xx redirects since Node's `http.get` does not do this automatically + +## Prerequisites + +1. **Network connection**: The BrightSign player should be connected to a network with access to the file server. +2. **SD card**: The destination SD card should have enough free space for the downloaded file. + +## Configuration + +Edit the constants at the top of the ` + +