HLS Sistemi, Adaptive Bitrate, Soft Mode, Crossfade, Canli Log Analizi 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.
| 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.