Skip to content

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.

Terminal window
curl -b jar -X POST \
http://localhost:9001/api/v1/apps/default/ads/promo-a \
--data-binary @promo.mp4

Ads are conditioned like VOD assets, so the splice is clean. GET /api/v1/apps/default/ads reports readiness.

Terminal window
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.

For a playout channel, set ads and ad_every on the playlist and breaks happen at item boundaries automatically. See VOD and playout.

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:

  1. Send the feed over SRT, as MPEG-TS with its SCTE-35 stream. RTMP cannot carry the markers.
  2. 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:
Terminal window
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:

MarkerEffect
splice_insert, out of networkStarts a break on the frame it names, or at once if it is immediate.
splice_insert, back inEnds the break on the frame it names.
time_signal with a break, advertisement, placement opportunity or ad block startStarts a break, the same way.
The matching end segmentation typeEnds the break.
A cancelled eventWithdraws 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.

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:

Terminal window
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.

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:

MacroValue
[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.

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.

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).

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.

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:

02.345Z
#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
AttributeMeaning
IDUnique 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>.
CLASSAlways com.jadarit.ad.
START-DATEWhen the ad starts, matching the date of the segment it starts in.
PLANNED-DURATIONThe length the break was asked for, in seconds.
DURATIONThe 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-NAMEThe 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.

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.

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.