# CDN Integration EasyStream supports multi-CDN video delivery for global reach, reduced latency, and bandwidth optimization. ## Supported CDN Providers - 🐰 **BunnyCDN** - Cost-effective, global edge network - ☁️ **Cloudflare R2** - Zero egress fees, S3-compatible - 🪣 **AWS S3 + CloudFront** - Enterprise-grade, highly scalable - 🔵 **Backblaze B2** - Affordable S3-compatible storage - 🟢 **Wasabi** - Predictable pricing, no egress fees - 🔧 **Custom** - Any S3-compatible storage provider ## Features - 🌍 **Multi-CDN Support** - Upload to multiple providers for redundancy - 📊 **Automatic Failover** - Falls back to secondary CDN if primary fails - 🎬 **Multi-Quality Delivery** - Serve 360p, 480p, 720p, 1080p, 1440p, 2160p - 💾 **Bandwidth Tracking** - Monitor usage and costs per provider - 🔄 **Cache Purging** - Invalidate CDN cache on video updates - 🚀 **Auto Quality Selection** - Pick best quality based on connection speed - 💰 **Cost Optimization** - Use cheapest provider for your traffic patterns ## Setup ### 1. Database Migration ```bash docker-compose exec db mysql -u easystream -peasystream easystream < __install/migrations/005_add_cdn_support.sql ``` ### 2. Configure CDN Provider Add your CDN provider credentials to the database: ```sql INSERT INTO db_cdn_providers ( provider_name, provider_type, api_key, storage_zone, pull_zone, cdn_hostname, region, is_active, priority ) VALUES ( 'BunnyCDN Production', 'bunny', 'YOUR_API_KEY', 'easystream-videos', 'easystream', 'easystream.b-cdn.net', 'de', 1, 0 ); ``` **Provider Types:** - `bunny` - BunnyCDN - `cloudflare` - Cloudflare R2 - `s3` - AWS S3 - `backblaze` - Backblaze B2 - `wasabi` - Wasabi - `custom` - Custom S3-compatible **Priority:** Lower = higher priority (0 is primary) ### 3. Add Frontend Assets Include quality selector on video watch page: ```html ``` ### 4. Upload Videos to CDN ```bash # Upload single video php app_scripts/cdn/upload_to_cdn.php 123 # Upload all pending videos php app_scripts/cdn/upload_to_cdn.php --all ``` ### 5. Automate Uploads (Optional) Add to upload workflow in `upload.php`: ```php require_once 'f_core/f_classes/class.cdn.php'; $cdn = new VCDN(); // After video upload $cdn->uploadVideo($video_id, $local_file_path, 'original'); ``` Or run background worker via supervisor/systemd. ## CDN Provider Configuration ### BunnyCDN 1. Sign up at [bunny.net](https://bunny.net) 2. Create a storage zone 3. Create a pull zone linked to the storage zone 4. Get your API key from Account Settings ```sql INSERT INTO db_cdn_providers ( provider_name, provider_type, api_key, storage_zone, pull_zone, cdn_hostname ) VALUES ( 'BunnyCDN', 'bunny', 'YOUR_API_KEY', 'your-storage-zone', 'your-pull-zone', 'your-pull-zone.b-cdn.net' ); ``` **Cost:** ~$0.01/GB storage + $0.01-0.05/GB bandwidth (varies by region) ### Cloudflare R2 1. Sign up for Cloudflare account 2. Enable R2 in dashboard 3. Create a bucket 4. Generate API token ```sql INSERT INTO db_cdn_providers ( provider_name, provider_type, api_key, api_secret, storage_zone, region ) VALUES ( 'Cloudflare R2', 'cloudflare', 'YOUR_ACCESS_KEY', 'YOUR_SECRET_KEY', 'your-bucket', 'auto' ); ``` **Cost:** $0.015/GB storage, **zero egress fees** ### AWS S3 + CloudFront 1. Create S3 bucket in AWS Console 2. Create CloudFront distribution 3. Generate IAM access keys ```sql INSERT INTO db_cdn_providers ( provider_name, provider_type, api_key, api_secret, storage_zone, cdn_hostname, region ) VALUES ( 'AWS S3', 's3', 'YOUR_ACCESS_KEY', 'YOUR_SECRET_KEY', 'your-bucket', 'd123abc.cloudfront.net', 'us-east-1' ); ``` **Cost:** $0.023/GB storage + $0.085/GB bandwidth (first 10TB) ### Backblaze B2 1. Sign up at [backblaze.com](https://www.backblaze.com/b2) 2. Create a bucket 3. Generate application key ```sql INSERT INTO db_cdn_providers ( provider_name, provider_type, api_key, api_secret, storage_zone, region ) VALUES ( 'Backblaze B2', 'backblaze', 'YOUR_KEY_ID', 'YOUR_APP_KEY', 'your-bucket', 'us-west-004' ); ``` **Cost:** $0.006/GB storage + $0.01/GB bandwidth (first 3x storage free) ### Wasabi 1. Sign up at [wasabi.com](https://wasabi.com) 2. Create a bucket 3. Generate access keys ```sql INSERT INTO db_cdn_providers ( provider_name, provider_type, api_key, api_secret, storage_zone, region ) VALUES ( 'Wasabi', 'wasabi', 'YOUR_ACCESS_KEY', 'YOUR_SECRET_KEY', 'your-bucket', 'us-east-1' ); ``` **Cost:** $5.99/TB/month (storage + bandwidth included) ## Usage ### Get CDN URL ```http GET /api/cdn.php?action=url&video_id=123&quality=720p ``` **Response:** ```json { "success": true, "url": "https://easystream.b-cdn.net/videos/123/720p/video.mp4" } ``` ### Get Available Qualities ```http GET /api/cdn.php?action=qualities&video_id=123 ``` **Response:** ```json { "success": true, "qualities": [ { "quality_label": "auto", "resolution": "Auto", "cdn_url": null }, { "quality_label": "1080p", "resolution": "1920x1080", "cdn_url": "https://easystream.b-cdn.net/videos/123/1080p/video.mp4" }, { "quality_label": "720p", "resolution": "1280x720", "cdn_url": "https://easystream.b-cdn.net/videos/123/720p/video.mp4" } ] } ``` ### Upload to CDN ```http POST /api/cdn.php?action=upload Content-Type: application/json { "video_id": 123, "local_path": "/srv/uploads/video.mp4", "quality": "original" } ``` ### Purge Cache ```http POST /api/cdn.php?action=purge Content-Type: application/json { "video_id": 123 } ``` ### Bandwidth Usage ```http GET /api/cdn.php?action=bandwidth&days=30&provider_id=1 ``` **Response:** ```json { "success": true, "data": [ { "date": "2026-03-01", "bandwidth_mb": 15234.56, "requests": 4567 } ] } ``` ## Frontend Integration ### Quality Selector The quality selector automatically initializes on video watch pages: ```javascript const selector = new QualitySelector(videoId, { playerElement: document.querySelector('video'), containerElement: document.querySelector('.video-controls') }); ``` **Features:** - Auto quality based on connection speed - User preference persistence (localStorage) - Seamless quality switching (preserves playback position) ### Programmatic Quality Switch ```javascript const selector = window.qualitySelector; selector.switchQuality('720p', 'https://cdn.example.com/video_720p.mp4'); ``` ## Multi-Quality Encoding Generate multiple quality levels from source video: ```bash # Using ffmpeg ffmpeg -i input.mp4 \ -vf scale=1920:1080 -c:v libx264 -b:v 5000k -preset medium output_1080p.mp4 \ -vf scale=1280:720 -c:v libx264 -b:v 2500k -preset medium output_720p.mp4 \ -vf scale=854:480 -c:v libx264 -b:v 1000k -preset medium output_480p.mp4 \ -vf scale=640:360 -c:v libx264 -b:v 500k -preset medium output_360p.mp4 ``` Then upload each quality: ```php $qualities = ['1080p', '720p', '480p', '360p']; foreach ($qualities as $quality) { $cdn->uploadVideo($video_id, "output_{$quality}.mp4", $quality); } ``` ## Bandwidth Tracking Track CDN bandwidth usage automatically: ```php $cdn->trackBandwidth($provider_id, $video_id, $bytes_transferred); ``` Integrate with analytics: ```php // After video view $file_size = filesize($video_path); $cdn->trackBandwidth($provider_id, $video_id, $file_size); ``` ## Cost Optimization ### Provider Selection Strategy 1. **High Traffic, US/EU audience:** BunnyCDN or Cloudflare R2 2. **Predictable costs:** Wasabi (flat rate) 3. **Low traffic, budget-conscious:** Backblaze B2 4. **Enterprise, existing AWS:** S3 + CloudFront ### Multi-Provider Setup Configure multiple providers with priority: ```sql -- Primary (lowest cost for your region) INSERT INTO db_cdn_providers (..., priority) VALUES (..., 0); -- Secondary (failover) INSERT INTO db_cdn_providers (..., priority) VALUES (..., 1); ``` System automatically falls back to secondary if primary fails. ### Bandwidth Cost Estimates | Provider | 1TB/month | 10TB/month | 100TB/month | |----------|-----------|------------|-------------| | BunnyCDN | $10-50 | $100-400 | $1,000-2,500 | | Cloudflare R2 | $15 | $150 | $1,500 | | AWS S3+CF | $85 | $815 | $6,815 | | Backblaze B2 | $6 + $10 | $6 + $70 | $6 + $700 | | Wasabi | $6 | $60 | $600 | *Estimates vary by region and traffic patterns* ## Database Schema ### db_cdn_providers | Column | Type | Description | |--------|------|-------------| | provider_id | INT | Primary key | | provider_name | VARCHAR(50) | Display name | | provider_type | ENUM | bunny/cloudflare/s3/backblaze/wasabi/custom | | api_key | VARCHAR(255) | API key | | api_secret | VARCHAR(255) | API secret (for S3-compatible) | | storage_zone | VARCHAR(100) | Bucket/zone name | | pull_zone | VARCHAR(100) | CDN distribution name | | cdn_hostname | VARCHAR(255) | CDN URL hostname | | region | VARCHAR(50) | Geographic region | | is_active | TINYINT | Enabled status | | priority | INT | Selection priority (lower = higher) | ### db_video_qualities | Column | Type | Description | |--------|------|-------------| | quality_id | INT | Primary key | | video_id | INT | Video ID | | quality_label | VARCHAR(20) | 360p, 480p, 720p, 1080p, etc. | | resolution | VARCHAR(20) | Width x height | | bitrate | INT | Video bitrate (kbps) | | file_path | VARCHAR(512) | Local file path | | cdn_url | VARCHAR(512) | CDN URL | | file_size | BIGINT | File size in bytes | | codec | VARCHAR(50) | Video codec (h264, vp9, av1) | ### db_cdn_cache_status | Column | Type | Description | |--------|------|-------------| | cache_id | INT | Primary key | | video_id | INT | Video ID | | provider_id | INT | CDN provider ID | | quality_label | VARCHAR(20) | Quality level | | cdn_url | VARCHAR(512) | CDN URL | | status | ENUM | pending/uploading/ready/failed | | upload_progress | INT | Upload progress (%) | | error_message | TEXT | Error details | ## Performance - **CDN latency:** 10-50ms (vs 200-500ms local) - **Upload speed:** Varies by provider (10-100 MB/s typical) - **Cache hit ratio:** 85-95% (after warmup) - **Failover time:** <1 second (automatic) ## Troubleshooting ### Upload fails with "Permission denied" 1. Check API credentials are correct 2. Verify storage zone/bucket exists 3. Check firewall allows outbound HTTPS ### Video URL returns 404 1. Check CDN cache status: `SELECT * FROM db_cdn_cache_status WHERE video_id = X` 2. Verify file uploaded successfully to CDN 3. Check CDN hostname is correct ### Quality selector doesn't appear 1. Verify JavaScript is loaded 2. Check browser console for errors 3. Ensure video has multiple qualities available ### Bandwidth tracking inaccurate 1. Check analytics integration is active 2. Verify `trackBandwidth()` called on video views 3. Review CDN provider's own bandwidth reports ## Advanced Features ### Adaptive Bitrate Streaming (HLS/DASH) Generate HLS playlist for adaptive streaming: ```bash ffmpeg -i input.mp4 \ -vf scale=1920:1080 -c:v libx264 -b:v 5000k -f hls -hls_time 6 -hls_list_size 0 output_1080p.m3u8 \ -vf scale=1280:720 -c:v libx264 -b:v 2500k -f hls -hls_time 6 -hls_list_size 0 output_720p.m3u8 ``` ### Geo-Routing Route users to nearest CDN edge: ```php $user_country = $_SERVER['HTTP_CF_IPCOUNTRY'] ?? 'US'; $cdn_region = $user_country === 'US' ? 'us-east' : 'eu-west'; // Select provider by region $provider = getProviderByRegion($cdn_region); ``` ### Signed URLs (Hotlink Protection) Generate time-limited signed URLs: ```php function generateSignedUrl($url, $expiry = 3600) { $secret = 'YOUR_SIGNING_SECRET'; $expires = time() + $expiry; $token = hash_hmac('sha256', $url . $expires, $secret); return "{$url}?token={$token}&expires={$expires}"; } ``` ## Future Enhancements - [ ] Automatic quality encoding on upload - [ ] HLS/DASH adaptive streaming - [ ] Geo-routing based on user location - [ ] Signed URLs for DRM/hotlink protection - [ ] CDN analytics dashboard - [ ] Cost alerts (bandwidth thresholds) - [ ] Video preview thumbnails (storyboards) - [ ] AV1 codec support - [ ] CDN warmup on publish - [ ] Multi-region redundancy ## Learn More - [BunnyCDN Documentation](https://docs.bunny.net/) - [Cloudflare R2 Docs](https://developers.cloudflare.com/r2/) - [AWS S3 Best Practices](https://docs.aws.amazon.com/AmazonS3/latest/userguide/best-practices.html) - [FFmpeg Encoding Guide](https://trac.ffmpeg.org/wiki/Encode/H.264)