πŸ“š Technical Documentation

Interactive Video Player with HLS.js, AWS MediaTailor, Datazoom Analytics and Brightline

🎯 System Overview

This application demonstrates a complete video streaming solution with ad insertion, real-time analytics, and interactive advertising. It combines multiple technologies to create a premium video experience similar to platforms like YouTube or Hulu.

Core Technologies

πŸ—οΈ System Architecture

Data Flow

1. Video Request β†’ Browser requests HLS stream from MediaTailor
2. MediaTailor Session β†’ Datazoom creates personalized session with tracking URLs
3. Playback β†’ HLS.js plays video with inserted ads
4. Ad Detection β†’ Datazoom detects ad markers in manifest
5. Linear Ads β†’ Video ads with countdown timer
6. Non-Linear Ads β†’ Brightline displays interactive overlays
7. Analytics β†’ All events sent to Datazoom/Grafana

πŸ“¦ Main Components

1. Video Players

The application includes two players to demonstrate different configurations:

Player Features Use Case
Video 1 - Standard HLS.js only, no ads Basic video playback
Video 2 - Premium HLS.js + MediaTailor + Datazoom + Brightline Full experience with ads and analytics

2. Event System

The on-screen event monitor displays real-time activity:

3. Countdown Timer

During linear ads (video ads), a countdown timer displays that:

πŸ”§ SDK Integration

HLS.js - Video Player

const hls = new Hls();
hls.loadSource(url);
hls.attachMedia(video);

HLS.js handles adaptive bitrate (ABR) video playback, automatically selecting the best quality based on bandwidth.

Datazoom - Analytics and MediaTailor

// Initialization
datazoom.init({ 
    configuration_id: "2477d200-37bd-4c42-910d-22389350c3bd"
});

// Create MediaTailor session
const session = await datazoom.mediatailor.createSession({
    sessionInitUrl: sessionInitPrefix
});

// Create analytics context
const context = datazoom.createContext(hls, {
    adapterOptions: { hlsClass: Hls }
});

// Attach MediaTailor session
context.attachMediaTailorSession(session);

Datazoom acts as a bridge between HLS.js and MediaTailor, capturing ad events and sending metrics.

Brightline - Interactive Ads

// Device configuration
BL.on_deviceInfo = function() {
    return {
        configId: "1018",
        applicationName: "Mercado Libre Video Player",
        // ... more config
    };
};

// Event callbacks
BL.on_BL_expanded = function() { video.pause(); };
BL.on_BL_collapsed = function() { video.play(); };

// Initialize SDK
BL.init();

// Open interactive ad
BL.openAd(url, videoId, true, 'mercado-libre', trackingData, duration);

Brightline handles interactive overlays that appear over the video without interrupting playback.

🎬 Ad Types

Linear Ads (Video Ads)

Video ads that interrupt main content:

Non-Linear Ads (Overlay Ads)

Interactive ads that overlay the video:

Pause Ads

Ads that appear when user pauses the video:

πŸ“Š Tracking System

MediaTailor Tracking URLs

MediaTailor provides tracking URLs for each ad:

These URLs are passed to Brightline in the trackingNodeStr parameter for automatic firing by the SDK.

Tracking Data Visualization

The "View MediaTailor Tracking URLs" button displays:

🎨 Styles and UI

Animated Header

The header title uses the slideInRight animation that slides text from the right on page load.

Player Boxes with Hover Effect

Video containers have an elevation effect on hover, creating a sense of depth.

Event Log Terminal-Style

The event monitor uses a terminal style with:

Brightline Container

The #BL_Container has special z-index and pointer-events configuration to allow overlays to appear over video while permitting clicks.

βš™οΈ Key JavaScript Functions

initPlayer(videoId, url, enableDatazoom, sessionPrefix)

Main function that initializes a video player with optional ad insertion and analytics:

Basic HLS.js Setup

const video = document.getElementById(videoId);
const hls = new Hls();
hls.loadSource(url);
hls.attachMedia(video);

Creates an HLS.js instance and attaches it to the video element. HLS.js handles adaptive bitrate streaming automatically.

Pause Ad Implementation (Video 2 only)

let pauseAdShown = false;
video.addEventListener('pause', () => {
    if (pauseAdShown || video.ended || video.currentTime === 0) return;
    pauseAdShown = true;
    BL.openAd(pauseAdUrl, videoId, true, 'mercado-libre-pause', '{}', 0);
});

// Listen for resume message from ad creative
window.addEventListener('message', (event) => {
    if (event.data.action === 'resumeVideo') {
        video.play();
        pauseAdShown = false;
    }
});

Key Points:

MediaTailor Session Creation

const sessionConfig = { sessionInitUrl: sessionPrefix };
const session = await datazoom.mediatailor.createSession(sessionConfig);

// Create Datazoom analytics context
const context = datazoom.createContext(hls, {
    adapterOptions: { hlsClass: Hls }
});

// Link MediaTailor session to Datazoom for ad tracking
context.attachMediaTailorSession(session);

// Get personalized playback URL with ad markers
const playbackUrl = session.getPlaybackUrl();
hls.loadSource(playbackUrl);

What This Does:

Linear Ad Event Handling

session.addEventListener(datazoom.mediatailor.SessionUiEvent.AD_START, event => {
    const duration = event.detail?.adObject?.adData?.duration || 0;
    
    // Start countdown timer
    if (duration > 0) {
        startAdCountdown(duration);
    }
    
    // Store tracking data for inspection
    const adObject = event.detail?.adObject;
    if (adObject) {
        latestTrackingData = {
            timestamp: new Date().toISOString(),
            type: 'LINEAR_AD',
            duration: duration,
            adData: adObject.adData,
            trackingEvents: adObject.adData?.trackingEvents || []
        };
    }
    
    // Disable controls during ad
    video.controls = false;
});

session.addEventListener(datazoom.mediatailor.SessionUiEvent.AD_END, event => {
    stopAdCountdown();
    video.controls = true;
});

Implementation Notes:

Non-Linear Ad Event Handling

session.addEventListener(datazoom.mediatailor.SessionUiEvent.NONLINEAR_AD_START, event => {
    const adData = event.detail?.nonLinearAdsObject?.adData;
    if (!adData) return;
    
    const { nonLinearAdList, trackingEvents, duration } = adData;
    const nonLinearAd = nonLinearAdList[0];
    
    // Store tracking data
    latestTrackingData = {
        timestamp: new Date().toISOString(),
        type: 'NON_LINEAR_AD',
        duration: duration,
        trackingUrls: trackingEvents
    };
    
    // Select random creative
    const adCreatives = [
        'https://cdn.mediaplaypen.com/meli/ad/index.html',
        'https://cdn.mediaplaypen.com/meli/ad/index2.html'
    ];
    const overrideUrl = adCreatives[Math.floor(Math.random() * adCreatives.length)];
    
    // Prepare tracking for Brightline
    const trackingNodeStr = JSON.stringify({
        trackingEvents: {
            createView: trackingEvents
                .filter(e => e.eventType === 'impression')
                .map(e => e.beaconUrls)
                .flat()
        }
    });
    
    // Open Brightline overlay
    BL.openAd(
        overrideUrl,           // Creative URL
        videoId,               // Video element ID
        true,                  // Enable interactivity
        'mercado-libre',       // Campaign ID
        trackingNodeStr,       // Tracking URLs
        duration               // Auto-close duration
    );
    
    // Fallback timeout (in case Brightline doesn't auto-close)
    setTimeout(() => {
        BL.closeAd();
    }, (duration + 2) * 1000);
});

Critical Implementation Details:

startAdCountdown(duration) / stopAdCountdown()

function startAdCountdown(duration) {
    const countdown = document.getElementById('adCountdown');
    const timeDisplay = document.getElementById('adCountdownTime');
    let timeLeft = Math.floor(duration);
    
    countdown.style.display = 'block';
    
    function updateCountdown() {
        const mins = Math.floor(timeLeft / 60);
        const secs = timeLeft % 60;
        timeDisplay.textContent = `${mins}:${secs.toString().padStart(2, '0')}`;
        
        if (timeLeft > 0) {
            timeLeft--;
        } else {
            stopAdCountdown();
        }
    }
    
    updateCountdown();
    adCountdownInterval = setInterval(updateCountdown, 1000);
}

function stopAdCountdown() {
    document.getElementById('adCountdown').style.display = 'none';
    if (adCountdownInterval) {
        clearInterval(adCountdownInterval);
        adCountdownInterval = null;
    }
}

How It Works:

logEvent(type, message, action)

function logEvent(type, message, action) {
    const logContent = document.getElementById('event-log-content');
    const time = new Date().toLocaleTimeString('es-ES');
    const entry = document.createElement('div');
    entry.className = 'event-entry';
    entry.innerHTML = `
        <span class="event-time">[${time}]</span>
        <span class="event-type">${type}</span> 
        ${message}
        ${action ? `<span class="event-action">β†’ ${action}</span>` : ''}
    `;
    logContent.appendChild(entry);
    
    // Auto-scroll to bottom
    const logBox = logContent.parentElement;
    setTimeout(() => {
        logBox.scrollTop = logBox.scrollHeight;
    }, 0);
}

Usage Examples:

logEvent('SISTEMA', 'Page loaded', 'Initializing SDKs');
logEvent('DATAZOOM', 'MediaTailor session created', session.id);
logEvent('BRIGHTLINE', 'Opening interactive overlay', `Duration: ${duration}s`);

showTrackingUrls() / closeTrackingOverlay()

function showTrackingUrls() {
    const overlay = document.getElementById('trackingOverlay');
    const content = document.getElementById('trackingDetailsContent');
    
    if (latestTrackingData) {
        content.textContent = JSON.stringify(latestTrackingData, null, 2);
    } else {
        content.textContent = 'No tracking data available yet.';
    }
    
    overlay.style.display = 'flex';
}

function closeTrackingOverlay() {
    document.getElementById('trackingOverlay').style.display = 'none';
}

// Close on outside click
document.addEventListener('DOMContentLoaded', () => {
    const overlay = document.getElementById('trackingOverlay');
    overlay.addEventListener('click', (e) => {
        if (e.target === overlay) {
            closeTrackingOverlay();
        }
    });
});

Key Features:

πŸ”„ Detailed Event Flow

Linear Ad (Video Ad)

  1. MediaTailor inserts ad marker in HLS manifest
  2. Datazoom detects marker and fires AD_START event
  3. JavaScript captures event and extracts duration
  4. Countdown timer starts with startAdCountdown(duration)
  5. Video controls are disabled (video.controls = false)
  6. Tracking URLs stored in latestTrackingData
  7. Ad plays normally
  8. On completion, MediaTailor fires AD_END event
  9. Countdown stops with stopAdCountdown()
  10. Controls re-enabled (video.controls = true)

Non-Linear Ad (Overlay Ad)

  1. MediaTailor inserts non-linear ad marker in manifest
  2. Datazoom detects marker and fires NONLINEAR_AD_START event
  3. JavaScript extracts tracking URLs and duration
  4. Randomly selects one of two Mercado Libre creatives
  5. Prepares trackingNodeStr object with impression URLs
  6. Calls BL.openAd() with creative URL and tracking data
  7. Brightline displays overlay over video (video continues playing)
  8. Fallback timeout set to close ad after duration + 2 seconds
  9. Brightline automatically closes overlay when duration expires
  10. NONLINEAR_AD_END event is ignored (to avoid premature closes)

Pause Ad

  1. User pauses video (Video 2 only)
  2. Event listener detects pause event
  3. Verifies pause ad hasn't been shown in this session
  4. Calls BL.openAd() with pause ad creative
  5. User sees ad and can click "Resume"
  6. Creative sends postMessage with action: 'resumeVideo'
  7. JavaScript captures message and calls video.play()
  8. Resets pauseAdShown flag

πŸ› Error Handling and Edge Cases

Known Issues and Solutions

Logging and Debugging

The system includes extensive logging:

πŸ“ˆ Analytics and Dashboards

Grafana Dashboard

Visualizes real-time metrics:

CloudWatch Dashboard

Monitors AWS infrastructure:

πŸš€ Configuration and Deployment

Configuration URLs

// Video 1 - Standard stream
const url1 = 'https://fcd796e21ed48a1abb4824e834c02632.p05sqb.channel-assembly.mediatailor.us-west-2.amazonaws.com/v1/channel/ModernAdsTestStream/index.m3u8';

// Video 2 - MediaTailor stream
const url2 = 'https://36820097a1284a0c9e2fa0344b33a58e.mediatailor.us-west-2.amazonaws.com/v1/master/bb5755666fd4f9f014f0687a72e9a3726a78e563/Meli/index.m3u8';

// Session init for Datazoom
const sessionInitPrefix = 'https://36820097a1284a0c9e2fa0344b33a58e.mediatailor.us-west-2.amazonaws.com/v1/session/bb5755666fd4f9f014f0687a72e9a3726a78e563/Meli/index.m3u8';

Configuration IDs

Ad Creatives

πŸ” Security and Best Practices

πŸ“ Final Notes

This implementation demonstrates a complete integration of multiple video advertising technologies. Key points: