Skip to content

Use Cases IPTV

Thomas Mangin edited this page Nov 15, 2025 · 1 revision

IPTV and Multicast Distribution

ExaBGP enables efficient IPTV and video distribution using multicast BGP, Multicast VPN, and optimized content delivery.

Table of Contents

Overview

IPTV Requirements

IPTV and video distribution systems need:

  • Efficient multicast: One stream to many receivers
  • Quality of Service: Prioritize video traffic
  • Channel management: Dynamic channel lineup changes
  • Multi-tenancy: Separate content per customer/subscriber
  • Source redundancy: Failover between video sources
  • Geographic distribution: Regional content delivery

ExaBGP Role

ExaBGP enables IPTV distribution by:

  1. Multicast source advertisement: Announce video sources via BGP
  2. Multicast VPN: Isolate IPTV traffic per customer/tenant
  3. Source failover: Automatically switch between video sources
  4. Channel control: Dynamically add/remove channels

Important: ExaBGP announces multicast routes but does NOT handle IGMP/PIM or actual multicast forwarding. Your router or multicast daemon handles packet replication.

Multicast BGP

Basic Multicast Announcement

Advertise multicast source:

#!/usr/bin/env python3
import sys

# Video source configuration
VIDEO_SOURCES = {
    'hd-sports': {
        'group': '239.1.1.10',
        'source': '10.1.1.100',
        'rate': '8000'  # kbps
    },
    'hd-news': {
        'group': '239.1.1.11',
        'source': '10.1.1.101',
        'rate': '8000'
    },
    'sd-general': {
        'group': '239.1.1.20',
        'source': '10.1.1.102',
        'rate': '3000'
    }
}

NEXT_HOP = "10.1.1.1"

# Announce multicast sources
for channel, config in VIDEO_SOURCES.items():
    # Announce (S,G) state via BGP
    print(f"announce route {config['group']}/32 "
          f"next-hop {NEXT_HOP} "
          f"origin igp", flush=True)

while True:
    line = sys.stdin.readline().strip()
    if not line:
        break

Source Redundancy

Failover between primary and backup video sources:

#!/usr/bin/env python3
import sys
import time
import socket

# Channel configuration
CHANNEL = {
    'group': '239.1.1.10',
    'primary_source': '10.1.1.100',
    'backup_source': '10.1.1.101'
}

NEXT_HOP_PRIMARY = "10.1.1.1"
NEXT_HOP_BACKUP = "10.1.1.2"

def check_video_source(source_ip, port=5000):
    """Check if video source is streaming"""
    try:
        sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        sock.settimeout(2)
        sock.bind(('', 0))

        # Try to receive multicast traffic
        import struct
        group = socket.inet_aton(CHANNEL['group'])
        source = socket.inet_aton(source_ip)

        # Set socket options for multicast
        sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP,
                        group + source)

        # Check for traffic
        sock.recvfrom(1024)
        sock.close()
        return True
    except:
        return False

# Monitor sources and switch
current_source = 'primary'
announced = False

while True:
    primary_ok = check_video_source(CHANNEL['primary_source'])
    backup_ok = check_video_source(CHANNEL['backup_source'])

    if primary_ok and current_source != 'primary':
        # Switch to primary
        if announced:
            print(f"withdraw route {CHANNEL['group']}/32 "
                  f"next-hop {NEXT_HOP_BACKUP}", flush=True)
        print(f"announce route {CHANNEL['group']}/32 "
              f"next-hop {NEXT_HOP_PRIMARY}", flush=True)
        current_source = 'primary'
        announced = True

    elif not primary_ok and backup_ok and current_source != 'backup':
        # Switch to backup
        if announced:
            print(f"withdraw route {CHANNEL['group']}/32 "
                  f"next-hop {NEXT_HOP_PRIMARY}", flush=True)
        print(f"announce route {CHANNEL['group']}/32 "
              f"next-hop {NEXT_HOP_BACKUP}", flush=True)
        current_source = 'backup'
        announced = True

    time.sleep(5)

Multicast VPN for IPTV

Per-Customer IPTV

Isolate IPTV streams per customer using Multicast VPN:

#!/usr/bin/env python3
import sys

# Customer IPTV configurations
CUSTOMERS = {
    'customer-a': {
        'vrf': 'CUST-A-IPTV',
        'rd': '10.1.1.1:100',
        'rt': '65000:100',
        'channels': {
            'sports': '239.100.1.1',
            'news': '239.100.1.2',
            'movies': '239.100.1.3'
        }
    },
    'customer-b': {
        'vrf': 'CUST-B-IPTV',
        'rd': '10.1.1.1:200',
        'rt': '65000:200',
        'channels': {
            'sports': '239.200.1.1',
            'news': '239.200.1.2',
            'entertainment': '239.200.1.4'
        }
    }
}

NEXT_HOP = "10.1.1.1"

# Announce per-customer channels
for customer, config in CUSTOMERS.items():
    rd = config['rd']
    rt = config['rt']

    for channel_name, group in config['channels'].items():
        # Announce multicast group in customer VRF
        print(f"announce route {group}/32 "
              f"next-hop {NEXT_HOP} "
              f"route-distinguisher {rd} "
              f"route-target {rt}", flush=True)

while True:
    line = sys.stdin.readline().strip()
    if not line:
        break

Regional Content Delivery

Deliver different content per region:

#!/usr/bin/env python3
import sys

# Regional channel lineup
REGIONS = {
    'us-east': {
        'rd': '10.1.0.1:1',
        'rt': '65000:1',
        'channels': ['239.1.1.1', '239.1.1.2', '239.1.1.3']
    },
    'us-west': {
        'rd': '10.2.0.1:1',
        'rt': '65000:2',
        'channels': ['239.2.1.1', '239.2.1.2', '239.2.1.3']
    },
    'eu': {
        'rd': '10.3.0.1:1',
        'rt': '65000:3',
        'channels': ['239.3.1.1', '239.3.1.2', '239.3.1.3']
    }
}

# Determine region
import socket
hostname = socket.gethostname()
region = None
for r in REGIONS:
    if r in hostname:
        region = r
        break

if region:
    config = REGIONS[region]
    for group in config['channels']:
        print(f"announce route {group}/32 "
              f"next-hop self "
              f"route-distinguisher {config['rd']} "
              f"route-target {config['rt']}", flush=True)

while True:
    line = sys.stdin.readline().strip()
    if not line:
        break

QoS Considerations

Mark IPTV Traffic

Use communities to mark IPTV traffic for QoS:

#!/usr/bin/env python3
import sys

# QoS classes
QOS_CLASSES = {
    '4K-premium': {
        'community': '65000:10',  # Highest priority
        'dscp': 46  # EF
    },
    'HD': {
        'community': '65000:20',
        'dscp': 34  # AF41
    },
    'SD': {
        'community': '65000:30',
        'dscp': 26  # AF31
    }
}

# Channels with QoS
CHANNELS = {
    '4k-sports': {
        'group': '239.1.1.1',
        'qos': '4K-premium'
    },
    'hd-news': {
        'group': '239.1.1.2',
        'qos': 'HD'
    },
    'sd-general': {
        'group': '239.1.1.3',
        'qos': 'SD'
    }
}

NEXT_HOP = "10.1.1.1"

# Announce with QoS communities
for channel, config in CHANNELS.items():
    qos_class = QOS_CLASSES.get(config['qos'])
    if qos_class:
        print(f"announce route {config['group']}/32 "
              f"next-hop {NEXT_HOP} "
              f"community [{qos_class['community']}]", flush=True)

while True:
    line = sys.stdin.readline().strip()
    if not line:
        break

Bandwidth Management

Announce available bandwidth for admission control:

#!/usr/bin/env python3
import sys
import psutil

# Video source capacity
MAX_BANDWIDTH_MBPS = 1000
CHANNEL_BANDWIDTH_MBPS = 8  # HD channel

def get_available_bandwidth():
    """Calculate available IPTV bandwidth"""
    net_io = psutil.net_io_counters()
    # Simplified: check interface utilization
    # In production, query actual multicast stream count
    return MAX_BANDWIDTH_MBPS

def announce_capacity():
    """Announce available capacity to admission control"""
    available = get_available_bandwidth()
    max_channels = int(available / CHANNEL_BANDWIDTH_MBPS)

    # Use extended community for capacity
    # This is a simplified example
    print(f"announce route 10.1.1.1/32 "
          f"next-hop self "
          f"community [65000:100]", flush=True)

announce_capacity()

while True:
    line = sys.stdin.readline().strip()
    if not line:
        break

Configuration Examples

IPTV Headend

Configuration (/etc/exabgp/iptv-headend.conf):

process iptv-channels {
    run python3 /etc/exabgp/iptv-announce.py;
    encoder json;
}

neighbor 10.0.0.1 {
    router-id 10.1.1.1;
    local-address 10.1.1.1;
    local-as 65001;
    peer-as 65001;

    family {
        ipv4 multicast;
    }

    api {
        processes [ iptv-channels ];
    }
}

neighbor 10.0.0.2 {
    router-id 10.1.1.1;
    local-address 10.1.1.1;
    local-as 65001;
    peer-as 65001;

    family {
        ipv4 multicast;
    }

    api {
        processes [ iptv-channels ];
    }
}

Multicast VPN Provider Edge

Configuration (/etc/exabgp/mvpn-pe.conf):

process mvpn-controller {
    run python3 /etc/exabgp/mvpn-announce.py;
    encoder text;
}

neighbor 10.0.0.1 {
    router-id 10.1.1.1;
    local-address 10.1.1.1;
    local-as 65001;
    peer-as 65001;

    family {
        ipv4 mpls-vpn;
        ipv4 multicast-vpn;
    }

    api {
        processes [ mvpn-controller ];
    }
}

Dynamic Channel Provisioning

API Program (/etc/exabgp/iptv-announce.py):

#!/usr/bin/env python3
import sys
import json
import time

# Channel database (simplified)
ACTIVE_CHANNELS = {}

def load_channel_lineup():
    """Load channel lineup from config/database"""
    return {
        'hd-1': {'group': '239.1.1.1', 'source': '10.1.1.100'},
        'hd-2': {'group': '239.1.1.2', 'source': '10.1.1.101'},
        'sd-1': {'group': '239.1.1.10', 'source': '10.1.1.102'}
    }

def announce_channel(channel_id, group, source):
    """Announce channel availability"""
    print(f"announce route {group}/32 next-hop {source}", flush=True)
    ACTIVE_CHANNELS[channel_id] = {'group': group, 'source': source}

def withdraw_channel(channel_id):
    """Remove channel from lineup"""
    if channel_id in ACTIVE_CHANNELS:
        config = ACTIVE_CHANNELS[channel_id]
        print(f"withdraw route {config['group']}/32 "
              f"next-hop {config['source']}", flush=True)
        del ACTIVE_CHANNELS[channel_id]

# Initial channel lineup
channels = load_channel_lineup()
for channel_id, config in channels.items():
    announce_channel(channel_id, config['group'], config['source'])

# Listen for channel changes (from EPG system, etc.)
while True:
    line = sys.stdin.readline().strip()
    if not line:
        continue

    try:
        command = json.loads(line)
        if command.get('action') == 'add-channel':
            announce_channel(
                command['channel_id'],
                command['group'],
                command['source']
            )
        elif command.get('action') == 'remove-channel':
            withdraw_channel(command['channel_id'])
    except:
        pass

    # Periodic refresh
    time.sleep(60)

See Also

Address Families

Related Use Cases

Configuration

Features


πŸ‘» Ghost written by Claude (Anthropic AI)

Clone this wiki locally