how to implement Server-Side Ad Insertion (SSAI) using VAST
Great! Let's dive into **VAST (Video Ad Serving Template)** and how to implement **Server-Side Ad Insertion (SSAI)** using VAST. This is a powerful method used by streaming platforms (like Hulu, YouTube TV, or Twitch) to seamlessly deliver video ads without relying on client-side ad players.
---
## 🔍 What is VAST?
**VAST** is an XML-based protocol created by the IAB (Interactive Advertising Bureau) to standardize how video ads are served across different platforms. It defines the structure of ad responses, including media files, tracking URLs, durations, and interactive elements.
- VAST works with **client-side ad insertion (CSAI)** by default.
- But it can be used in **SSAI (Server-Side Ad Insertion)** workflows when integrated correctly.
---
## 🔄 What is SSAI (Server-Side Ad Insertion)?
**SSAI**, also known as **Dynamic Ad Insertion (DAI)**, stitches ads directly into the video stream on the server **before** it reaches the viewer. This means:
- The viewer receives a **single, continuous stream** (e.g., HLS or DASH) with ads already embedded.
- Ads are less likely to be blocked by ad blockers.
- Better control over ad experience, timing, and quality.
- Ideal for live and on-demand streaming.
---
## 🧩 How VAST Fits into SSAI
In SSAI, **VAST is used to *source* ad content and metadata**, but **not played directly on the client**. Instead:
1. The **SSAI server** requests a VAST response from an ad decision server (ADS).
2. The VAST XML tells the SSAI system:
- Which ad creative(s) to use (video URLs)
- Duration
- Tracking URLs (impression, quartiles, etc.)
3. The SSAI system **fetches and stitches** the ad into the main content stream.
4. The final stream (with ads) is delivered to the client via HLS/DASH.
---
## ✅ Step-by-Step: Implementing SSAI with VAST
Here’s how to implement SSAI using VAST:
### 1. Set Up an SSAI Platform (or Use a Service)
You can:
- Use a commercial SSAI provider: **Google Ad Manager (with DAI)**, **AWS MediaTailor**, **Brightcove SSAI**, **JW Player DAI**, **Mux**, etc.
- Or build your own SSAI system using open-source tools (e.g., **FFmpeg**, **Shaka Packager**, **nginx-rtmp**, or **Red5 Pro**).
> For most use cases, **AWS MediaTailor** or **Google Ad Manager** are recommended due to mature VAST support.
---
### 2. Configure Ad Decision Server (ADS) to Return VAST
Your ad server (e.g., Google Ad Manager, FreeWheel, or a custom one) must return a **valid VAST 4.x response** when queried.
Example VAST snippet:
```xml
<VAST version="4.2">
<Ad id="123">
<InLine>
<AdSystem>My Ad Server</AdSystem>
<AdTitle>Coffee Ad</AdTitle>
<Impression>https://tracker.example.com/impression</Impression>
<Creative>
<Linear>
<Duration>00:00:30</Duration>
<MediaFiles>
<MediaFile delivery="progressive" type="video/mp4" width="1280" height="720" bitrate="3000">
https://ads.example.com/coffee-ad.mp4
</MediaFile>
</MediaFiles>
<TrackingEvents>
<Tracking event="start">https://tracker.example.com/start</Tracking>
<Tracking event="firstQuartile">https://tracker.example.com/firstQuartile</Tracking>
<Tracking event="midpoint">https://tracker.example.com/midpoint</Tracking>
<Tracking event="thirdQuartile">https://tracker.example.com/thirdQuartile</Tracking>
<Tracking event="complete">https://tracker.example.com/complete</Tracking>
</TrackingEvents>
</Linear>
</Creative>
</InLine>
</Ad>
</VAST>
```
---
### 3. SSAI Server Fetches VAST & Processes Ads
Your SSAI system:
- Makes an HTTP request to the ad tag URL (e.g., `https://pubads.g.doubleclick.net/gampad/ads?...`)
- Parses the VAST XML response.
- Extracts:
- Ad media URL(s)
- Duration
- Bitrate/resolution info (for quality matching)
- Downloads or proxies the ad video.
- Stitches the ad into the content stream.
> For HLS: The SSAI server generates a new playlist (.m3u8) with `#EXT-X-DISCONTINUITY` and ad segments.
> For DASH: It manipulates the MPD manifest and segments.
---
### 4. Serve the Combined Stream
The client (player) receives a **seamless stream** that looks like:
```
[Pre-roll Ad] → [Main Content] → [Mid-roll Ad] → [Content] → [Post-roll]
```
The player **doesn’t know** it’s seeing ads — it just plays a continuous stream.
---
### 5. Track Impressions & Events
Even though ads are server-side, **tracking still happens**:
- The SSAI server **pings VAST impression and tracking URLs** on behalf of the viewer.
- You can also forward user/device data (via macros like `[CACHEBUSTING]`, `[IPADDRESS]`, etc.).
- Optional: Forward tracking to your analytics backend.
> ⚠️ You are responsible for calling impression and quartile tracking beacons from the **server**.
Example:
```http
GET https://tracker.example.com/impression?cb=123456
```
---
### 6. Handle Ad Rules & Fallbacks
- If VAST returns `No Ads Available`, SSAI can skip the ad break.
- Use backup ad creatives if primary fails.
- Respect ad pod durations and pacing.
---
## 🛠 Tools & Services That Help
| Tool/Service | Role in SSAI + VAST |
|----------------------|---------------------|
| **AWS MediaTailor** | Fully managed SSAI; supports VAST/VPAID; inserts ads into HLS/DASH |
| **Google Ad Manager (DAI)** | Enterprise-grade; uses VAST; supports live & VOD |
| **JW Player DAI** | Easy integration; uses VAST from ad servers |
| **Mux Video + Ads** | Developer-friendly SSAI with VAST support |
| **THEOplayer, Bitmovin, Video.js (with plugins)** | Players that work with SSAI streams |
---
## ✅ Best Practices
1. **Use VAST 4.2+** for better error handling, support for ads pods, and companion ads.
2. **Validate VAST responses** using IAB VAST Validator: https://www.iab.com/guidelines/vast/
3. **Match ad bitrate/resolution** to content to avoid quality jumps.
4. **Cache VAST responses** and ad creatives to reduce latency.
5. **Secure ad URLs** (use signed URLs or tokens).
6. **Log ad decisions** for auditing and troubleshooting.
---
## 🧪 Testing Your SSAI + VAST Setup
- Use **sample VAST tags** from Google:
https://developers.google.com/interactive-media-ads/docs/sdks/html5/client-side/tags
- Test with **VLC or Safari** to view HLS streams.
- Monitor tracking beacons with tools like **Charles Proxy** or server logs.
- Validate manifests for correct `#EXT-X-AD-INFO` or ad markers (if supported).
---
## 📚 Summary
| Component | Role in SSAI with VAST |
|------------------|------------------------|
| VAST | Provides ad metadata and media URLs |
| SSAI Server | Fetches VAST, stitches ads into stream |
| Ad Server | Returns VAST responses based on targeting |
| Client Player | Plays the combined stream (unaware of ads) |
| Tracking | Server-side beacon calls to VAST tracking URLs |
---
## 📌 Example Flow
```
---
## 🔍 What is VAST?
**VAST** is an XML-based protocol created by the IAB (Interactive Advertising Bureau) to standardize how video ads are served across different platforms. It defines the structure of ad responses, including media files, tracking URLs, durations, and interactive elements.
- VAST works with **client-side ad insertion (CSAI)** by default.
- But it can be used in **SSAI (Server-Side Ad Insertion)** workflows when integrated correctly.
---
## 🔄 What is SSAI (Server-Side Ad Insertion)?
**SSAI**, also known as **Dynamic Ad Insertion (DAI)**, stitches ads directly into the video stream on the server **before** it reaches the viewer. This means:
- The viewer receives a **single, continuous stream** (e.g., HLS or DASH) with ads already embedded.
- Ads are less likely to be blocked by ad blockers.
- Better control over ad experience, timing, and quality.
- Ideal for live and on-demand streaming.
---
## 🧩 How VAST Fits into SSAI
In SSAI, **VAST is used to *source* ad content and metadata**, but **not played directly on the client**. Instead:
1. The **SSAI server** requests a VAST response from an ad decision server (ADS).
2. The VAST XML tells the SSAI system:
- Which ad creative(s) to use (video URLs)
- Duration
- Tracking URLs (impression, quartiles, etc.)
3. The SSAI system **fetches and stitches** the ad into the main content stream.
4. The final stream (with ads) is delivered to the client via HLS/DASH.
---
## ✅ Step-by-Step: Implementing SSAI with VAST
Here’s how to implement SSAI using VAST:
### 1. Set Up an SSAI Platform (or Use a Service)
You can:
- Use a commercial SSAI provider: **Google Ad Manager (with DAI)**, **AWS MediaTailor**, **Brightcove SSAI**, **JW Player DAI**, **Mux**, etc.
- Or build your own SSAI system using open-source tools (e.g., **FFmpeg**, **Shaka Packager**, **nginx-rtmp**, or **Red5 Pro**).
> For most use cases, **AWS MediaTailor** or **Google Ad Manager** are recommended due to mature VAST support.
---
### 2. Configure Ad Decision Server (ADS) to Return VAST
Your ad server (e.g., Google Ad Manager, FreeWheel, or a custom one) must return a **valid VAST 4.x response** when queried.
Example VAST snippet:
```xml
<VAST version="4.2">
<Ad id="123">
<InLine>
<AdSystem>My Ad Server</AdSystem>
<AdTitle>Coffee Ad</AdTitle>
<Impression>https://tracker.example.com/impression</Impression>
<Creative>
<Linear>
<Duration>00:00:30</Duration>
<MediaFiles>
<MediaFile delivery="progressive" type="video/mp4" width="1280" height="720" bitrate="3000">
https://ads.example.com/coffee-ad.mp4
</MediaFile>
</MediaFiles>
<TrackingEvents>
<Tracking event="start">https://tracker.example.com/start</Tracking>
<Tracking event="firstQuartile">https://tracker.example.com/firstQuartile</Tracking>
<Tracking event="midpoint">https://tracker.example.com/midpoint</Tracking>
<Tracking event="thirdQuartile">https://tracker.example.com/thirdQuartile</Tracking>
<Tracking event="complete">https://tracker.example.com/complete</Tracking>
</TrackingEvents>
</Linear>
</Creative>
</InLine>
</Ad>
</VAST>
```
---
### 3. SSAI Server Fetches VAST & Processes Ads
Your SSAI system:
- Makes an HTTP request to the ad tag URL (e.g., `https://pubads.g.doubleclick.net/gampad/ads?...`)
- Parses the VAST XML response.
- Extracts:
- Ad media URL(s)
- Duration
- Bitrate/resolution info (for quality matching)
- Downloads or proxies the ad video.
- Stitches the ad into the content stream.
> For HLS: The SSAI server generates a new playlist (.m3u8) with `#EXT-X-DISCONTINUITY` and ad segments.
> For DASH: It manipulates the MPD manifest and segments.
---
### 4. Serve the Combined Stream
The client (player) receives a **seamless stream** that looks like:
```
[Pre-roll Ad] → [Main Content] → [Mid-roll Ad] → [Content] → [Post-roll]
```
The player **doesn’t know** it’s seeing ads — it just plays a continuous stream.
---
### 5. Track Impressions & Events
Even though ads are server-side, **tracking still happens**:
- The SSAI server **pings VAST impression and tracking URLs** on behalf of the viewer.
- You can also forward user/device data (via macros like `[CACHEBUSTING]`, `[IPADDRESS]`, etc.).
- Optional: Forward tracking to your analytics backend.
> ⚠️ You are responsible for calling impression and quartile tracking beacons from the **server**.
Example:
```http
GET https://tracker.example.com/impression?cb=123456
```
---
### 6. Handle Ad Rules & Fallbacks
- If VAST returns `No Ads Available`, SSAI can skip the ad break.
- Use backup ad creatives if primary fails.
- Respect ad pod durations and pacing.
---
## 🛠 Tools & Services That Help
| Tool/Service | Role in SSAI + VAST |
|----------------------|---------------------|
| **AWS MediaTailor** | Fully managed SSAI; supports VAST/VPAID; inserts ads into HLS/DASH |
| **Google Ad Manager (DAI)** | Enterprise-grade; uses VAST; supports live & VOD |
| **JW Player DAI** | Easy integration; uses VAST from ad servers |
| **Mux Video + Ads** | Developer-friendly SSAI with VAST support |
| **THEOplayer, Bitmovin, Video.js (with plugins)** | Players that work with SSAI streams |
---
## ✅ Best Practices
1. **Use VAST 4.2+** for better error handling, support for ads pods, and companion ads.
2. **Validate VAST responses** using IAB VAST Validator: https://www.iab.com/guidelines/vast/
3. **Match ad bitrate/resolution** to content to avoid quality jumps.
4. **Cache VAST responses** and ad creatives to reduce latency.
5. **Secure ad URLs** (use signed URLs or tokens).
6. **Log ad decisions** for auditing and troubleshooting.
---
## 🧪 Testing Your SSAI + VAST Setup
- Use **sample VAST tags** from Google:
https://developers.google.com/interactive-media-ads/docs/sdks/html5/client-side/tags
- Test with **VLC or Safari** to view HLS streams.
- Monitor tracking beacons with tools like **Charles Proxy** or server logs.
- Validate manifests for correct `#EXT-X-AD-INFO` or ad markers (if supported).
---
## 📚 Summary
| Component | Role in SSAI with VAST |
|------------------|------------------------|
| VAST | Provides ad metadata and media URLs |
| SSAI Server | Fetches VAST, stitches ads into stream |
| Ad Server | Returns VAST responses based on targeting |
| Client Player | Plays the combined stream (unaware of ads) |
| Tracking | Server-side beacon calls to VAST tracking URLs |
---
## 📌 Example Flow
```
Client → Requests stream ↓ SSAI Server → Queries Ad Server (gets VAST) ↓ Parses VAST → Downloads ad video ↓ Stitches ad into content (HLS/DASH) ↓ Serves combined stream to client ↓ Pings VAST tracking URLs (impression, quartiles, etc.)
```
---
---
### ✅ 1. **When should I call the VAST impression URL on the server side?**
You should call the **`<Impression>`** URL(s) **when the ad begins to play** — that is, **as soon as the ad is stitched into the stream and starts being delivered to the viewer**.
#### 🕒 Timing:
- **Not when you request the VAST.**
- **Not when you receive the VAST.**
- ✅ **When the ad actually starts playing (i.e., the first frame is served).**
This is equivalent to a "client-side" impression, but since the client doesn’t know about the ad, **your SSAI server must trigger it**.
#### 📌 Best Practice:
Call the `<Impression>` URL:
- After successfully inserting the ad into the manifest/playlist.
- Before or at the same time the first ad segment is sent to the player.
- Only once per ad (do not call multiple times).
> 🔁 If the same ad is shown to multiple users, **call the impression URL once per viewer/session** (not once globally).
---
### ✅ 2. **What is `<Tracking event="start">` in VAST? Should I call it like the impression URL?**
Yes — **you should call it**, and **yes — it's similar to the impression**, but they serve **different purposes**.
Let’s clarify:
| Tracking Event | When to Trigger | Purpose |
|---------------------|------------------|--------|
| `<Impression>` | When ad begins playing | Logs that an impression occurred (used for billing/reporting). Often from ad server. |
| `<Tracking event="start">` | When first frame of ad is played | Confirms the ad playback started (part of engagement tracking). |
#### 🔍 Key Difference:
- **`<Impression>`**: Often used by the **ad server** to count inventory.
- **`<Tracking event="start">`**: Used by **analytics/verification vendors** (e.g., IAS, DoubleVerify) to confirm the ad actually *started playing* (not just scheduled).
> 📌 In many VAST responses, `<Impression>` and `<Tracking event="start">` may point to **different endpoints** (e.g., different vendors).
---
### ✅ So, What Should You Do?
In **SSAI**, your server must **manually call** all of these tracking URLs at the correct time:
| Event | When to Call | Called by SSAI Server? |
|------|---------------|------------------------|
| `<Impression>` | At ad start (first frame) | ✅ Yes |
| `<Tracking event="start">` | At ad start | ✅ Yes |
| `<Tracking event="firstQuartile">` | At 25% of ad duration | ✅ Yes |
| `<Tracking event="midpoint">` | At 50% | ✅ Yes |
| `<Tracking event="thirdQuartile">` | At 75% | ✅ Yes |
| `<Tracking event="complete">` | When ad finishes playing | ✅ Yes |
---
### 🧪 Example: 30-second Ad
```xml
<Linear>
<Duration>00:00:30</Duration>
<Impression>https://adsrv.com/impression?cb=123</Impression>
<TrackingEvents>
<Tracking event="start">https://track.com/start?cb=456</Tracking>
<Tracking event="firstQuartile">https://track.com/1q?cb=789</Tracking>
<Tracking event="midpoint">https://track.com/mid?cb=101</Tracking>
<Tracking event="thirdQuartile">https://track.com/3q?cb=102</Tracking>
<Tracking event="complete">https://track.com/comp?cb=103</Tracking>
</TrackingEvents>
</Linear>
```
---
---
### ✅ 1. **When should I call the VAST impression URL on the server side?**
You should call the **`<Impression>`** URL(s) **when the ad begins to play** — that is, **as soon as the ad is stitched into the stream and starts being delivered to the viewer**.
#### 🕒 Timing:
- **Not when you request the VAST.**
- **Not when you receive the VAST.**
- ✅ **When the ad actually starts playing (i.e., the first frame is served).**
This is equivalent to a "client-side" impression, but since the client doesn’t know about the ad, **your SSAI server must trigger it**.
#### 📌 Best Practice:
Call the `<Impression>` URL:
- After successfully inserting the ad into the manifest/playlist.
- Before or at the same time the first ad segment is sent to the player.
- Only once per ad (do not call multiple times).
> 🔁 If the same ad is shown to multiple users, **call the impression URL once per viewer/session** (not once globally).
---
### ✅ 2. **What is `<Tracking event="start">` in VAST? Should I call it like the impression URL?**
Yes — **you should call it**, and **yes — it's similar to the impression**, but they serve **different purposes**.
Let’s clarify:
| Tracking Event | When to Trigger | Purpose |
|---------------------|------------------|--------|
| `<Impression>` | When ad begins playing | Logs that an impression occurred (used for billing/reporting). Often from ad server. |
| `<Tracking event="start">` | When first frame of ad is played | Confirms the ad playback started (part of engagement tracking). |
#### 🔍 Key Difference:
- **`<Impression>`**: Often used by the **ad server** to count inventory.
- **`<Tracking event="start">`**: Used by **analytics/verification vendors** (e.g., IAS, DoubleVerify) to confirm the ad actually *started playing* (not just scheduled).
> 📌 In many VAST responses, `<Impression>` and `<Tracking event="start">` may point to **different endpoints** (e.g., different vendors).
---
### ✅ So, What Should You Do?
In **SSAI**, your server must **manually call** all of these tracking URLs at the correct time:
| Event | When to Call | Called by SSAI Server? |
|------|---------------|------------------------|
| `<Impression>` | At ad start (first frame) | ✅ Yes |
| `<Tracking event="start">` | At ad start | ✅ Yes |
| `<Tracking event="firstQuartile">` | At 25% of ad duration | ✅ Yes |
| `<Tracking event="midpoint">` | At 50% | ✅ Yes |
| `<Tracking event="thirdQuartile">` | At 75% | ✅ Yes |
| `<Tracking event="complete">` | When ad finishes playing | ✅ Yes |
---
### 🧪 Example: 30-second Ad
```xml
<Linear>
<Duration>00:00:30</Duration>
<Impression>https://adsrv.com/impression?cb=123</Impression>
<TrackingEvents>
<Tracking event="start">https://track.com/start?cb=456</Tracking>
<Tracking event="firstQuartile">https://track.com/1q?cb=789</Tracking>
<Tracking event="midpoint">https://track.com/mid?cb=101</Tracking>
<Tracking event="thirdQuartile">https://track.com/3q?cb=102</Tracking>
<Tracking event="complete">https://track.com/comp?cb=103</Tracking>
</TrackingEvents>
</Linear>
```
Your SSAI server should: 1. **At 0s (ad starts)**: - Call: `https://adsrv.com/impression?cb=123` - Call: `https://track.com/start?cb=456` 2. **At 7.5s (25%)**: - Call: `https://track.com/1q?cb=789` 3. **At 15s (50%)**: - Call: `https://track.com/mid?cb=101` 4. **At 22.5s (75%)**: - Call: `https://track.com/3q?cb=102` 5. **At 30s (end)**: - Call: `https://track.com/comp?cb=103`
> ⚠️ Use accurate timing based on actual ad duration. You can schedule these calls using timers or event hooks in your streaming pipeline.
---
### 🔁 Optional: Use Macros
Many tracking URLs include macros like:
- `[CACHEBUSTING]` → Replace with random number
- `[CONTENTPLAYHEAD]` → Current time in playback
- `[IPADDRESS]` → Viewer’s IP
Example:
```
https://track.com/start?cb=[CACHEBUSTING]&ip=[IPADDRESS]
```
You should **resolve these macros** before making the request.
---
### ✅ Summary: Your Checklist
| Task | Done? |
|------|-------|
| Call `<Impression>` at ad start | ✅ |
| Call `<Tracking event="start">` at ad start | ✅ |
| Schedule quartile events at correct times | ✅ |
| Call `<Tracking event="complete">` at end | ✅ |
| Replace VAST macros (like `[CACHEBUSTING]`) | ✅ |
| Log tracking calls for debugging | ✅ |
---
Let me know if you want a **Node.js/Python code example** for scheduling and calling these tracking events in an SSAI system!