HLS Sistemi, Adaptive Bitrate, Soft Mode, Crossfade, Canlı Log Analizi, 4 AI Sentez Raporu, Uygulanan Fix'ler ve Tam Mimari Analiz
Muzibu'nun muzik calar sistemi. Spotify veya Apple Music'teki calar gibi dusunun. Sarki secersiniz, calar, sonraki sarkiya gecer, ses ayarlarsiniz.
HLS (HTTP Live Streaming) = Sarkiyi kucuk parcalara bolup gonderme yontemi. Netflix videolari nasil parca parca yukleniyorsa, sarkilar da oyle. Bant genisliginiz duserse kalite otomatik duser, yukselirse kalite artar. Neden onemli? Hem korsan indirmeyi zorlastirir, hem de internet hizina gore en iyi deneyimi sunar.
Interneti hizli olan kisiye yuksek kaliteli ses, yavas olan kisiye dusuk kaliteli ses gonderme. Otomatik. 4 seviye var: Ultra dusuk (32k), Dusuk (64k), Orta (128k), Yuksek (~250k). Neden onemli? Mobil verideyken az veri harcar, WiFi'deyken en iyi kaliteyi verir.
Bazi kullanicilara (kurumsal, cok kullanicili hesaplar) dusuk kaliteli ses sunma modu. Sunucu yukunu ve bant genisligi maliyetini azaltir. Neden onemli? 50+ kullanicili kurumsal hesaplarda sunucu maliyetini %60-70 dusurebilir.
Toplamda 26 sorun tespit edildi. Bunlardan 5'i kritik (acil duzeltilmeli), 9'u orta seviye, 12'si dusuk seviye iyilestirme. Kritik sorunlar: sifreleme dosya adi cakismasi, segment suresi tutarsizligi ve kalite filtresi hatasi.
public/themes/muzibu/js/player/ ├── core/ │ ├── player-core.js # Ana Alpine.js bileseni (8.514 satir) │ └── safe-storage.js # localStorage guvenli wrapper ├── features/ │ ├── api.js # Kimlikli fetch helper (401 handling) │ ├── auth.js # Giris/kayit sistemi │ ├── buffer-monitor.js # Buffer saglik takibi │ ├── device-profiler.js # Cihaz profilleme (RAM, CPU) │ ├── favorites.js # Favori sarki/playlist │ ├── old-device-checker.js # Eski cihaz tespiti │ ├── performance-debug.js # Performans izleme │ ├── play-helpers.js # Calma yardimcilari │ ├── session.js # Oturum yonetimi │ ├── spa-router.js # SPA sayfa gecisleri │ ├── speed-tester.js # Ag hiz testi │ └── spot-player.js # Kurumsal reklam oynatici └── lib/ └── hls.min.js # HLS.js v1.4.12
isLoggedIn -- Giris durumucurrentUser -- Kullanici bilgilerishowAuthModal -- Giris modaliisPlaying -- Caliyor mucurrentTime / duration -- Zamanvolume / isMuted -- SesprogressPercent -- IlerlemeisHlsStream -- HLS aktif mihlsCurrentLevel -- Kalite (0-3)isSlowConnection -- Yavas baglanticurrentStreamType -- "hls" / "mp3"HLS_SHARED_CONFIG = {
enableWorker: true, // Web Worker ile ayri thread
lowLatencyMode: false, // VOD, canli degil
autoStartLoad: false, // Manuel baslatma (ABR lock icin KRiTiK)
startLevel: 3, // Yuksek kaliteden basla
abrEwmaDefaultEstimate: 250000, // 250kbps baslangic tahmini
abrBandWidthUpFactor: 0.5, // Muhafazakar yukseltme (0.7->0.5)
abrBandWidthFactor: 0.8, // Muhafazakar dusurme (0.95->0.8)
maxBufferLength: 12, // 12sn buffer (GC dostu)
keyLoadPolicy: {
default: {
maxTimeToFirstByteMs: 10000, // Key yukleme timeout: 10sn
maxLoadTimeMs: 20000, // Key toplam timeout: 20sn
timeoutRetry: { maxNumRetry: 3, retryDelayMs: 1000 }
}
}
}
Problem: HLS.js v1.4.12'de key-loader race condition -- ayni key URI icin birden fazla fragment yuklenmesi keyUriToKeyInfo map'ini eziyor.
Cozum: 3 katmanli key onbellekleme sistemi:
_hlsKeyCache[]'e kaydetCachingKeyLoader -- HLS.js XHR'i intercept et, cache'ten senkron donduracquire() -- Havuzdan alrelease() -- Havuza geri verhls.stopLoad()hls.detachMedia()hls.removeAllListeners()hls.destroy()audio.load() -- MediaSource flushTetikleyiciler: HLS init hatasi, Fatal error, bufferAppendError, 60sn timeout
triggerMp3Fallback(reason):
1. HLS taint + release (pool'a geri ver)
2. Blob URL cleanup
3. safeAudioCleanup() -- Event handler temizleme
4. fallback_url varsa -> playWithHowler()
5. Toast: "HLS basarisiz, MP3 ile caliniyor..."
ffmpeg -i input.mp3 \
-c:a aac -b:a {bitrate}k \
-af "loudnorm + stereotools + eq + lowpass" \
-hls_key_info_file enc.keyinfo \
-hls_time 6 -hls_list_size 0 \
-hls_segment_filename "segment-%03d.ts" \
playlist.m3u8
| Seviye | Bitrate | Kanal | Sample Rate | Segment |
|---|---|---|---|---|
| ultralow | 32 kbps | Mono | 22.05 kHz | 4 sn |
| low | 64 kbps | Mono | 22.05 kHz | 4 sn |
| mid | 128 kbps | Stereo | 44.1 kHz | 4 sn |
| high | ~180-280 kbps | Stereo | 44.1 kHz | 6 sn |
GET /hls-key/muzibu/songs/{id}?token=...&sig=...&expires=...storage/tenant{id}/app/public/muzibu/hls/{songId}/enc.bin| Endpoint | Yetki | Aciklama |
|---|---|---|
/api/muzibu/songs/{id}/stream | Auth | Sifreli HLS/MP3 URL dondurur |
/hls/muzibu/songs/{id}/master.m3u8 | Signed | Master playlist (variant listesi) |
/hls/muzibu/songs/{id}/segment-*.ts | Public | AES-128 sifreli segment (auth yok) |
/hls-key/muzibu/songs/{id} | Signed+Rate | 16-byte AES-128 key (60req/dk) |
/audio/songs/{id}/{exp}/{sig} | Signed | CDN MP3 URL (path-based) |
/songs/{id}/track-start | Sanctum | Dinleme baslangici (play_id doner) |
/songs/{id}/track-hit | Sanctum | 30sn milestone (play_count++) |
/songs/{id}/track-end | Sanctum | Bitis/atlama (sendBeacon) |
startLevel: 3 -- Yuksek kaliteden baslar, gerekirse dusururabrEwmaDefaultEstimate: 250kbps -- EWMA algoritmasi ile olcumabrBandWidthUpFactor: 0.5 -- 2x bant genisligi gerekli (muhafazakar)abrBandWidthFactor: 0.8 -- %80 altina duserse kalite indirilirloadSource() oncesi -> _abrStartupLocked = true
MANIFEST_PARSED -> currentLevel = startLevel (sabit)
FRAG_BUFFERED -> unlockABR() -> currentLevel = -1 (oto)
user.audio_preference -- Admin panelden atananMUZIBU_DEFAULT_AUDIO_FORMAT=autoNe yapar? Master playlist'ten yuksek kalite variant'lari cikarir.
// SongStreamController::serveHls() if (soft=1) { allowed = ['ultralow', 'low'] // config'den // high ve mid kaldirilir master.m3u8 -> sadece ultralow + low }
Backend sifreli URL dondurur: response._ + response.__
Frontend XOR ile cozer -> stream_url, fallback_url, stream_type
Kod hazir ancak kapali. HLS keyLoadError sorunu cozuldu, artik acilabilir.
hlsAudio -- Aktif calan elementhlsAudioNext -- Preload / crossfade element_nextTrackInProgress eszamanlilik kilidifromNaturalEnd: true -> gapless gecis_safePlayFromQueue(): max 3 deneme + token yenileme_refillQueue()_fetchUniqueGenreSongs)
/songs/popular)
Algoritma: HMAC-SHA256
Base: "{path}|{user_id}|{expires}"
Key: config('app.key')
Dogrulama: sig === expected_sig && now < expires
Playlist ve key endpoint'lerinde zorunlu. Segment'ler AES-128 sifreli oldugu icin imzasiz sunulur (CDN uyumlu).
Muzik calarken her sarkida "sifre cozme hatasi" olusuyordu. Sarkinin sifresini cozmek icin gereken "anahtar"in (key) adresi yanlis yazilmis.
Player dogru adrese gidip anahtari aliyor (/hls-key/... endpoint'i). Ancak playlist dosyasi (sarkinin "tarifi") yanlis bir adrese yonlendiriyor: /storage/... klasorune.
Storage klasoru dogrudan erisime kapali (403 Forbidden). Dolayisiyla HLS.js key dosyasini alamiyor, sifre cozulemiyor, sarki calinmiyor.
Sonuc olarak HLS ile calma basarisiz oluyor ve MP3 yedek modu devreye giriyor. Kullanici sarkiyi dinleyebiliyor ama dusuk kalitede ve HLS'in avantajlari (adaptive bitrate, sifreleme) kullanilamiyor.
/hls-key/muzibu/songs/{id} -> 16 bytes key indirildi -> cache'e yazildiEXT-X-KEY satirini gorur -> URI alanini okur#EXT-X-KEY:METHOD=AES-128, URI="https://mztest.muzibu.com/storage/tenant1001/muzibu/hls/{id}/enc.bin", IV=0xac37483e0d64e537ae63f308d3d89c0a
/storage/ yolu dogrudan erisime kapali. Nginx/Apache tarafindan engelleniyor./hls-key/muzibu/songs/{id}/storage/tenant1001/muzibu/hls/{id}/enc.bintriggerMp3Fallback('keyLoadError') -> Howler.js ile MP3 calma🔑 Key pre-fetched OK (16 bytes) 🔑 Key loaded via XHR (391ms, 16 bytes) ⚠️ HLS ERROR: {"details":"keyLoadError","fatal":false,"response":null} 🔑 Non-fatal keyLoadError #1 | response:null | errMsg:after key load, decryptdata unset or changed ⚠️ HLS ERROR: {"details":"keyLoadError","fatal":true,"response":null} 🔴 HLS FATAL ERROR: {song_id: 34455, errorDetails: "keyLoadError"} 🔄 FATAL keyLoadError → loadSource recovery (hard lock)
response:null -- Bu, HLS.js'in key yuklerken hic bir HTTP yaniti alamadigini (veya 403 aldiktan sonra key'i eslestiremedigi icin "decryptdata unset or changed" hatasini) gosteriyor.
| Song ID | Sarki Adi | HLS Sonucu | Rebuffer | Kurtarma |
|---|---|---|---|---|
| 34455 | A Friend Like You | FAIL | REBUF:3 | MP3 Fallback |
| 34456 | A Great Day | FAIL | REBUF:3 | MP3 Fallback |
| 34457 | After Hours | FAIL | REBUF:3 | MP3 Fallback |
| 34458 | Afterimage | FAIL | REBUF:1 | MP3 Fallback |
| 34459 | All Again | FAIL | REBUF:3 | MP3 Fallback |
FFmpeg -hls_key_info_file parametresi ile enc.keyinfo dosyasini okur ve icindeki URI'yi playlist.m3u8'e yazar. Bu dosyanin ici:
# enc.keyinfo icerigi (3 satir): https://mztest.muzibu.com/storage/tenant1001/muzibu/hls/34455/enc.bin <-- YANLIS URI (Satir 1) /var/.../enc.bin <-- Key dosyasinin sunucu yolu (Satir 2) ac37483e0d64e537ae63f308d3d89c0a <-- IV degeri (Satir 3)
EXT-X-KEY satirina yaziyor. Storage URL oldugu icin 403 aliyor.
https://mztest.muzibu.com/hls-key/muzibu/songs/34455?token=...&sig=...&expires=...
serveHls() fonksiyonunda playlist icerigi donerken key URI'yi dinamik olarak signed endpoint'e replace eder.
// SongStreamController::serveHls() $content = file_get_contents($playlistPath); $content = str_replace( 'https://.../storage/.../enc.bin', route('hls-key', [...]) + signed params ); return response($content);
ConvertToHLSJob ve HLSService'de enc.keyinfo olusturulurken /hls-key/ endpoint URI'si yazilir.
// ConvertToHLSJob / HLSService $keyUri = url("/hls-key/muzibu/songs/{$songId}"); file_put_contents($keyInfoPath, $keyUri . "\n" . $keyFilePath . "\n" . $iv );
/pwa-icon/192.png bulunamiyor. PWA manifest'te tanimli ama dosya mevcut degil. Mobil "ana ekrana ekle" deneyimini bozuyor./stream, /track iceren) bloklanabilir. Kullanici tarafli sorun ancak log'da gorunuyor.
Aynı hatayı 4 farklı yapay zekaya analiz ettirdik. Her biri farklı açılardan baktı ve farklı sorunlar buldu. Hepsinin bulgularını birleştirerek çok katmanlı bir kök neden analizi oluşturduk. Tek bir AI'ın bulamayacağı derinlikte bir çözüm ortaya çıktı.
| AI | Odak Noktası | Bulunan Kök Neden | Önerilen Çözüm | Doğruluk |
|---|---|---|---|---|
| AI #1 | Prefetch + KEY_LOADING race | Prefetch ile fragment loading arasında race condition | Prefetch'i devre dışı bırak | Kısmi — Semptom, neden değil |
| AI #2 | CachingKeyLoader implementasyonu | Cache-serve kodu eksik — her zaman super.load() çağrılıyor | Cache hit durumunda senkron callback | Doğru — İkincil neden |
| AI #3 | keyUriToKeyInfo map + key URI replace | serveHls() key URI replace sırasında ?v= parametresi siliniyor → map çakışması | Variant parametresini koru | Kısmi — Tasarımla uyumsuz (aynı key URI bilinçli) |
| AI #4 | Alpine.js reactive proxy cascade | self.hls.currentLevel = X → Alpine proxy 4x setter cascade → double loadFragment | HLS instance'ını proxy dışında tut | Doğru — Üçüncül neden |
ConvertToHLSJob ve HLSService, enc.keyinfo dosyasında key URI olarak /storage/tenant.../enc.bin yazıyor. Bu URL doğrudan erişime kapalı (403 Forbidden). Playlist.m3u8'e bu yanlış URI gömülüyor.
CachingKeyLoader cache'e key yazıyor ama asla cache'den serve etmiyor. super.load() HER ZAMAN çağrılıyor → 300-400ms async XHR bekleme → fragment referansı değişebilir → decryptdata race condition.
self.hls.currentLevel = safeLvl satırı Alpine.js reactive proxy üzerinden geçiyor → setter 4 kez tetikleniyor → HLS.js double loadFragment → aynı segment için paralel yükleme.
6 farklı düzeltme yapıldı. Bunların 3'ü doğrudan keyLoadError sorununu çözüyor, 2'si gelecekte aynı sorunun tekrar oluşmasını engelliyor, 1'i de performans iyileştirmesi yapıyor.
callbacks.onSuccess() direkt çağrılıyor, super.load() atlanıyor. Async XHR bekleme süresi 0ms'e düşürüldü.if (cachedKey && cachedKey.byteLength === 16) { // Cache hit → senkron callback (0ms) callbacks.onSuccess(fakeResponse, fakeStats, context, null); return; // XHR atla }
/hls-key/muzibu/songs/{id} endpoint'i yazılıyor. Yeni encode edilen şarkılar doğru key URI ile oluşturuluyor.// ESKİ: url('storage/tenant.../enc.bin') → 403! // YENİ: $keyUri = "https://{$domain}/hls-key/muzibu/songs/{$songId}";
/stream/key/{id} formatı yerine /hls-key/muzibu/songs/{id} endpoint'i yazılıyor.self._rawHls.currentLevel = safeLvl kullanılarak Alpine reactive proxy bypass ediliyor. 4x setter cascade önleniyor.// ESKİ: self.hls.currentLevel = safeLvl (Alpine proxy → 4x cascade) // YENİ: self._rawHls.currentLevel = safeLvl (direkt HLS.js setter)
/hls-key/muzibu/songs/{id} formatına çevrildi./storage/, /stream/key/, /hls-key/ — hepsini signed endpoint'e replace ediyor.HLS keyLoadError fix'inin çalıştığını doğrula. Müzik çalarken şarkılar HLS ile sorunsuz çalmalı, MP3 fallback'e düşmemeli.
| Optimizasyon | Konum | Etki |
|---|---|---|
| G1 Lazy HLS.js yukleme | Satir 14-36 | JS bundle kuculur, sayfa hizlanir |
| Progress 250ms interval | Satir 6235 | Ana thread yuku %60 azalir |
| HLS Instance havuzu | Satir 548-678 | Instance olusturma/yok etme maliyeti sifir |
| Key pre-cache | Satir 352-454 | Key yukleme gecikmesi sifir |
| DOM cache (hlsAudio lookup) | Satir 1044-1060 | DOM sorgulari ~0 |
| Stream LRU cache | Satir 7289-7299 | Tekrar calma: ag istegi yok |
| Segment'ler session'siz | Route middleware | Paralel 30+ istek -> session kilidi yok |
| M3U8 disk cache (3600sn) | Controller | Dosya okuma maliyeti azalir |
| CDN uyumlu path-based URL | SignedUrlService | Cloudflare cache kullanilabilir |
| Buffer 12sn (GC dostu) | HLS config | Bellek baskisi azalir |
HLSService.php:125$targetBitrate degiskeni generateMasterPlaylist() cagrildiginda tanimsiz. calculateTargetBitrate() sonucu dis scope'a tasinmiyor.// calculateTargetBitrate() sonucunu sakla
$targetBitrate = $this->calculateTargetBitrate($originalBitrate);
// ... FFmpeg komutunu olustur ...
$highBitrate = (int) str_replace('k', '', $targetBitrate) * 1000;
$this->generateMasterPlaylist($storagePath, $highBitrate);
ConvertToHLSJob.php:138 vs HLSService.php:20-hls_time 6 -> HLSService: CHUNK_DURATION = 4SongStreamController.php:754-777playlist.m3u8 satirini kaldiriyor, #EXT-X-STREAM-INF header satirini birakiyor. Kalan header -> HLS.js'de "playlist not found" hatasi.
ConvertToHLSJob.php, HLSService.php (enc.keyinfo olusturma)https://mztest.muzibu.com/storage/tenant1001/muzibu/hls/{id}/enc.bin seklinde olusturuluyor. Bu URL storage dizinine isaret ediyor ve dogrudan erisim 403 Forbidden donuyor. Playlist.m3u8 icindeki EXT-X-KEY URI'si de bu yanlis adresi kullaniyor./hls-key/ signed endpoint yerine /storage/ yoluna isaret ediyor./storage/ yoluna isaret etmesi. Player'in pre-cache ettigi key URL'si (/hls-key/...) ile playlist icindeki URI (/storage/...) eslesmediginden CachingKeyLoader cache MISS yapiyor./hls-key/ endpoint'ine isaret etmeli?q=mp3_128 gonderiliyor ama backend'de bu parametreyi alan endpoint belirsiz.| Bilesen | Dosya Yolu | Satir |
|---|---|---|
| Ana Player | public/themes/muzibu/js/player/core/player-core.js | 8.514 |
| Stream Controller | Modules/Muzibu/App/Http/Controllers/Api/SongStreamController.php | ~800 |
| HLS Servisi | app/Services/Muzibu/HLSService.php | ~650 |
| Donusturme Job | Modules/Muzibu/App/Jobs/ConvertToHLSJob.php | ~250 |
| Signed URL | app/Services/SignedUrlService.php | ~180 |
| Variant Command | Modules/Muzibu/App/Console/Commands/AddHlsVariantsCommand.php | ~280 |
| Muzibu Config | Modules/Muzibu/config/config.php | ~80 |
| API Routes | Modules/Muzibu/routes/api.php | ~100 |
| Player Blade | resources/views/themes/muzibu/components/player.blade.php | ~500 |
| HLS.js Kutuphane | public/themes/muzibu/js/player/lib/hls.min.js | v1.4.12 |
/storage/ yoluna isaret ediyor. Tum sarkilarda %100 HLS basarisizlik. Cozum: serveHls'de runtime URI replace + enc.keyinfo'da dogru URI.