vod-ssai
v1.0.8
Published
Server-guided ad insertion (SGAI) proof of concept: Eyevinn sgai-ad-proxy, VAST bridge, HLS.js + IMA player, Docker Compose stack
Maintainers
Readme
SSAI / SGAI proof of concept
npm package
Install and use from Node.js or run the bundled Docker stack via CLI:
npm install vod-ssaiFor frontend-only integration (lightweight player embed helpers + static index.html), use:
import { createPlayerUrl, mountPlayer } from "vod-ssai/player-sdk";React integration
- Copy the player HTML once during setup/build:
mkdir -p public/player
cp node_modules/vod-ssai/player-sdk/index.html public/player/index.html- Use helper API:
import { createPlayerUrl } from "vod-ssai/player-sdk";
export function SgaiPlayerFrame({ videoId, apiBase }) {
const src = createPlayerUrl({
playerPath: "/player/index.html",
videoId,
apiBase,
});
return (
<iframe
title="SSAI Player"
src={src}
allow="autoplay; fullscreen; picture-in-picture"
style={{ width: "100%", aspectRatio: "16/9", border: 0 }}
/>
);
}Angular integration
- Add asset copy in
angular.json:
{
"glob": "index.html",
"input": "node_modules/vod-ssai/player-sdk",
"output": "/player"
}- Component usage:
import { Component } from "@angular/core";
import { DomSanitizer, SafeResourceUrl } from "@angular/platform-browser";
import { createPlayerUrl } from "vod-ssai/player-sdk";
@Component({
selector: "app-player-frame",
template: `<iframe [src]="src" allow="autoplay; fullscreen; picture-in-picture"></iframe>`,
})
export class PlayerFrameComponent {
src: SafeResourceUrl;
constructor(private sanitizer: DomSanitizer) {
const url = createPlayerUrl({
playerPath: "/player/index.html",
videoId: "VIDEO_UUID",
apiBase: "https://YOUR-SERVER.COM",
});
this.src = this.sanitizer.bypassSecurityTrustResourceUrl(url);
}
}import {
packageRoot,
DEFAULT_URLS,
DEFAULT_PORTS,
getPaths,
runDockerCompose,
} from "vod-ssai";
console.log(DEFAULT_URLS.player);
await runDockerCompose(["up", "--build", "-d"]);npx vod-ssai compose up --build -d
npx vod-ssai root
npx vod-ssai urlsYou still need Docker, generated hls-out/ under the package directory (see scripts), and a browser at the player URL.
Publish: log in with npm login, set "name" in package.json (use a scoped name if vod-ssai is taken), optionally add "repository" / "homepage", then run npm publish (public scoped packages: npm publish --access public).
This repository demonstrates server-guided ad insertion (SGAI) using Eyevinn sgai-ad-proxy: a VAST-backed proxy that rewrites your HLS manifest with interstitial breaks, plus a small HLS.js + Google IMA player that plays the stitched stream from a same-origin URL.
What you get in Docker
| Piece | Role |
|--------|------|
| hls-nginx | Serves generated VoD HLS (hls-out/) on host :8000 |
| vast-bridge | Minimal VAST 4.x HTTP service for the proxy (:8080); optional JSON hook to an ad decision API |
| sgai-ad-proxy | Rewrites master.m3u8 with SGAI interstitials (:3333) |
| sgai-player | Static player + reverse proxy so the browser hits :8090 (avoids CORS on interstitial requests) |
There is no separate “ad database” or bidding service in this compose: the POC is intentionally small so you can focus on manifest behavior and the player.
Quick start
chmod +x scripts/*.sh
./scripts/generate-hls-docker.sh # FFmpeg in Docker; outputs ./hls-out
docker compose up --build -dOpen http://127.0.0.1:8090/player/ in Chrome or Firefox (HLS.js with interstitials). Use the Ad tag dropdown to try Google sample VAST/VMAP, Eyevinn’s test ad, or same-origin URLs under /vast-proxy/ (proxied to vast-bridge).
Stop: docker compose down
More detail (Helm sketch, host ffmpeg, CLI notes): see POC.md.
SaaS console (per-video ad count + IMA)
Use docker-compose.saas.yml: config API, /admin UI, and three SGAI presets (1 / 2 / 5 mid-roll slots). See deployment/saas/README.md.
AWS (EC2 + S3 + CloudFront)
VoD can stay packaged as HLS in S3; sgai-ad-proxy pulls the master playlist URL over HTTPS (no re-encode required for hosting). EC2 runs the Docker stack that serves the player and proxy.
- Guide: deployment/aws/README.md
- Compose:
docker-compose.aws.ymlwith.env.awsfrom deployment/aws/env.example (CDN_ORIGIN= CloudFront hostname only; manifest paths such asvideos/video1/master.m3u8come from the SaaS console or player URL) - Upload HLS to S3:
./scripts/sync-hls-to-s3.sh s3://YOUR_BUCKET/videos/video1/(or any prefix that matches manifest_path in the console)
Why OpenAdServer is not in this docker-compose.yml
OpenAdServer (and similar forks) is a full ad stack: API service, PostgreSQL, Redis, often optional Nginx/Prometheus—its own docker-compose.yml with fixed ports and healthchecks.
We do not vendor that stack here for several practical reasons:
Port conflicts with this POC
This repo already maps host :8000 to hls-nginx (your VoD origin). Typical OpenAdServer compose maps :8000 to the ad-server container. Running both “as-is” on one machine collides unless you remap ports everywhere.Different responsibility
sgai-ad-proxy speaks VAST XML to decide what to stitch. OpenAdServer (today) is oriented around JSON APIs (e.g.POST /api/v1/ad/request), not a drop-in VAST server for the proxy. This repo’svast-bridgealready bridges that gap: it emits VAST and optionally callsOPENADS_URLfor targeting.Operational weight
Pulling Postgres + Redis + migrations + seed data into this compose would turn a manifest/player POC into a multi-team platform install: longer boot, more failure modes, and harder CI for anyone who only wants SGAI behavior.Upstream owns the contract
OpenAdServer’s Docker layout, env vars, and init scripts change with the fork. Embedding a copy here would duplicate maintenance and drift from the project you actually run.
Recommended pattern: run OpenAdServer from its repository (or your fork), then point this stack at it only when you need it:
# Example: OpenAdServer listening on host :8000
export OPENADS_URL=http://host.docker.internal:8000
docker compose up -dvast-bridge will see OPENADS_URL and can use JSON responses when they include a usable video URL on the creative; otherwise it keeps using DEFAULT_MEDIA_M3U8 for the VAST MediaFile.
URLs (defaults)
| URL | Meaning |
|-----|--------|
| http://127.0.0.1:8000/hls-out/master.m3u8 | Raw VoD (no SGAI) |
| http://127.0.0.1:3333/hls-out/master.m3u8 | SGAI-rewritten manifest |
| http://127.0.0.1:8090/player/ | Player (use this to see interstitials) |
| http://127.0.0.1:8080/vast / .../api/vast | VAST bridge (direct) |
If 8080 is taken on your machine, change the vast-bridge port mapping in docker-compose.yml and update the sgai-ad-proxy command VAST URL accordingly.
License
Third-party components (sgai-ad-proxy, HLS.js, IMA SDK, OpenAdServer) follow their respective licenses. This POC’s glue code is provided as-is for experimentation.
