Ad insertion
Server-side ad insertion puts the break inside the media timeline. There is no separate ad request from the browser, so there is nothing for a blocker to intercept and nothing to buffer awkwardly between content and commercial.
Ad insertion is a licensed feature, available during the 30-day trial. When a
licence that includes it lapses, starting a break answers
402 Payment Required and streams already on air keep running; see
Licensing.
Uploading an ad
Section titled “Uploading an ad”curl -b jar -X POST \ http://localhost:9001/api/v1/apps/default/ads/promo-a \ --data-binary @promo.mp4Ads are conditioned like VOD assets, so the splice is clean. GET /api/v1/apps/default/ads reports readiness.
Triggering a break
Section titled “Triggering a break”curl -b jar -X POST \ http://localhost:9001/api/v1/apps/default/streams/channel1/ad-break \ -H 'content-type: application/json' \ -d '{"ad":"promo-a","duration_secs":30}'The break lasts duration_secs, between 1 and 300 seconds. An ad shorter than
the break plays again; a longer one is cut off when the break ends. The break
ends on the stream’s next keyframe, so it can run up to one keyframe interval
over. Leave the field out and the break lasts
[ssai].default_break_secs, or the
application’s own default if it sets one.
The dashboard has the same control under each live stream, with a menu to pick the ad and set the duration.
Scheduled breaks
Section titled “Scheduled breaks”For a playout channel, set ads and ad_every on the playlist and breaks
happen at item boundaries automatically. See
VOD and playout.
Breaks from SCTE-35 markers
Section titled “Breaks from SCTE-35 markers”Broadcast playout systems mark their ad breaks with SCTE-35 markers inside the feed. Jadarit can follow them, so breaks happen exactly where your schedule puts them, with nobody pressing a button:
- Send the feed over SRT, as MPEG-TS with its SCTE-35 stream. RTMP cannot carry the markers.
- In the dashboard, open the application’s Settings, set Ad breaks from SCTE-35 to Follow the markers, and choose the ad to play. Or through the API:
curl -b jar -X PUT http://localhost:9001/api/v1/apps/default \ -H 'content-type: application/json' \ -d '{"scte35":{"enabled":true,"ad":"promo-a"}}'Leave ad empty to play the application’s first ready ad.
What the markers do:
| Marker | Effect |
|---|---|
splice_insert, out of network | Starts a break on the frame it names, or at once if it is immediate. |
splice_insert, back in | Ends the break on the frame it names. |
time_signal with a break, advertisement, placement opportunity or ad block start | Starts a break, the same way. |
| The matching end segmentation type | Ends the break. |
| A cancelled event | Withdraws a break that has not started yet. |
A break lasts the duration in its marker. A marker without one plays until its return marker, for at most 5 minutes. Playout systems send each marker several times in case one is lost; the repeats are recognised and start nothing more. Markers that are not about ad breaks, such as programme and chapter boundaries or heartbeats, are ignored.
Following markers is off for every application until you turn it on. A feed can carry markers its owner has never thought about, and a server that started playing ads into it unasked would be the worst kind of surprise. With it off, markers are still read and logged, which is a handy way to check what a feed carries before you rely on it.
Breaks you start yourself are unaffected: a marker never ends a break that you or your automation started through the API.
Ads from your ad server (VAST)
Section titled “Ads from your ad server (VAST)”With an ad server connected, each viewer gets the ads it chooses for them, the way they would from MediaTailor or Google DAI: Google Ad Manager, SpringServe, FreeWheel and any other server that speaks VAST 2, 3 or 4.
In the dashboard, open the application’s Settings, set Ads from an ad server to Ask the ad server, and paste your ad tag URL. Or through the API:
curl -b jar -X PUT http://localhost:9001/api/v1/apps/default \ -H 'content-type: application/json' \ -d '{"vast":{"enabled":true,"tag":"https://ads.example.com/vast?sid=[session.id]&dur=[avail.duration_seconds]"}}'Breaks start exactly as before: by hand, on a schedule, or from SCTE-35 markers. When one is announced, Jadarit asks your ad server for every viewer watching, over HLS and WebRTC alike, each with that viewer’s own details, so the answers are in before the break reaches anyone’s screen. A break you start by hand or on a schedule begins 3 seconds after you ask for it, to leave that time; SCTE-35 markers usually give more warning than that themselves.
What your ad server receives
Section titled “What your ad server receives”Every ad request and tracking call is made on the viewer’s behalf. It carries
their address in X-Forwarded-For and X-Device-IP and their browser’s
User-Agent, so the ad server targets and counts the view as theirs. Behind a
CDN or proxy, set trusted_proxies
so Jadarit knows the viewer’s real address.
These macros are filled in, per viewer, wherever they appear in the ad tag:
| Macro | Value |
|---|---|
[session.id] | The viewer’s session, the same for every break they watch. |
[stream.name], [stream.app] | The stream and its application. |
[avail.duration_seconds] | The break’s length. |
[avail.id] | The break, the same for every viewer of it. |
[player_params.<name>] | Any parameter the player added to the playlist URL, such as playlist.m3u8?device=tv for [player_params.device]. |
[CACHEBUSTING], [TIMESTAMP], [DEVICEUA], [DEVICEIP], [SERVERSIDE] | As the VAST standard defines them. |
Wrappers are followed up to five deep, and every wrapper’s impression, tracking and error URLs are called along with the ad’s own. Failures are reported to the ad server’s error URLs with the standard VAST error codes.
What each viewer sees
Section titled “What each viewer sees”The ads replace the house ad in each viewer’s own playlist. Ads that together fit the break play in order; an ad that does not fit is skipped, as VAST asks, and whatever time remains plays the house ad. If the broadcaster ends the break early, the programme comes back on time and the ad is cut there.
The first time Jadarit sees an ad, it downloads and prepares it: conditioned, encoded for every rendition, and cut into segments. That takes a few seconds, so the viewers of that break see the house ad, and from the next break on the ad plays. Prepared ads are kept on disk and survive restarts. A house ad is therefore always needed: upload one before connecting an ad server.
Impressions and progress (start, quartiles, completion) are reported as each viewer’s player is seen to reach them, so a viewer who leaves halfway through an ad is not counted as having finished it. Each is reported once per viewer, whichever quality they watched it in.
WebRTC viewers get their own ads too, swapped in on their own connection at sub-second latency. With adaptive bitrate on, a viewer gets the ad encoded for the quality they are watching, so one held to a small rendition by their connection is not sent a large ad. Their progress is reported as the ad is actually sent to them.
An ad server on your own network
Section titled “An ad server on your own network”Ad tags, tracking and video URLs pointing at private, local or link-local
addresses are refused by default. A URL from an ad server must not be able to
reach your own network or the server’s own API. If you run your ad server
yourself, on a private network, choose Allow a private network (or send
"allow_private": true).
What viewers see
Section titled “What viewers see”The break is signalled with a stream discontinuity, so conforming players handle the transition the way they were designed to. To the player it is one continuous stream that happens to change encoding parameters partway through.
With adaptive bitrate on, every rendition gets its own copy of the ad, cut at the same points, so a viewer who changes quality mid-break sees no jump.
Ad breaks in the HLS playlist
Section titled “Ad breaks in the HLS playlist”Every break is announced in the HLS playlist with an #EXT-X-DATERANGE tag,
and every segment carries its date in #EXT-X-PROGRAM-DATE-TIME, so a player
or analytics tool can tell when an ad is on screen. Use it to show an “Ad”
label, keep viewers from seeking past the break, or count ad views:
#EXT-X-DISCONTINUITY#EXT-X-DATERANGE:ID="scte35-1207-4",CLASS="com.jadarit.ad",START-DATE="2026-10-28T16:01:02.345Z",PLANNED-DURATION=30.000,DURATION=30.033,X-AD-NAME="promo-a"#EXTINF:2.000,00142.ts| Attribute | Meaning |
|---|---|
ID | Unique to the break. A break started by a marker includes the marker’s event id, scte35-<event id>-<n>; one started any other way is break-<n>. |
CLASS | Always com.jadarit.ad. |
START-DATE | When the ad starts, matching the date of the segment it starts in. |
PLANNED-DURATION | The length the break was asked for, in seconds. |
DURATION | The length it actually ran, added once the programme is back. A break ends on the programme’s next keyframe, so this can be slightly longer than planned. |
X-AD-NAME | The ad that played. |
Every rendition carries the same tag, so a player that changes quality during the break still sees one break. A break longer than the playlist window stays announced until it ends, so a viewer who joins halfway through still knows an ad is playing.
The tag describes an ad that is already in the video. It does not carry the
SCTE35-OUT attribute, which is what asks a downstream ad inserter to fill a
break, so a CDN or ad service in front of Jadarit will not play a second ad on
top of yours.
Overlays
Section titled “Overlays”For a sponsor’s logo or a promotion that stays on screen while the programme continues, rather than a break that replaces it, use overlays. They are drawn into the picture, so ad blockers cannot remove them either.
Clean feed for partners
Section titled “Clean feed for partners”If you restream to another platform, that partner usually should not receive your ad breaks. Jadarit keeps a clean copy of the pre-insertion feed available at the same time, so partners get the programme while your own audience gets the commercial. It is produced only when something is actually consuming it, so it costs nothing when unused.
Per-target control lives in the restream configuration; see Recording and restreaming.