v2 — 26 Şubat 2026

Production Ses Eşitleme + HLS Standardizasyon

2-pass loudnorm ses eşitleme + 4sn segment + v2 audio filtreler

Kod değişiklikleri + bağımsız re-encode script

v2'de Yeni: Kod Değişiklikleri

Bu değişiklikler test sunucusunda (mztest.muzibu.com) uygulandı. Production'a git pull ile taşınacak.

Güncellendi ConvertToHLSJob.php

Audio filtreler v1→v2: lowpass/stereotools/treble cut kaldırıldı (hışırtı nedeni). 2-pass loudnorm eklendi — HLSService::buildTwoPassFilter() kullanıyor.

Güncellendi AddHlsVariantsCommand.php

reEncodeHigh() metodu 2-pass loudnorm kullanıyor. --re-encode-high flag ile high playlist'i 4sn segment ile yeniden oluşturuyor.

Yeni Metod HLSService.php

buildTwoPassFilter(string $inputPath): string — Merkezi 2-pass loudnorm filtre oluşturucu. Pass 1: FFmpeg ile ses seviyesi ölçümü. Pass 2: Ölçülen değerlerle hassas normalizasyon. Hem job hem command bu metodu kullanır.

Güncellendi hls-streaming.blade.php

Dokümantasyon güncellendi: "EQ + LP Filter" → "Loudnorm v2 + Bass EQ". v2 filtre zinciri yansıtıldı.

Ölü kod temizliği: ConvertSongToHLS.php zaten silinmiş, /stream/key/ route zaten kaldırılmış — temiz.

2-Pass Loudnorm Nasıl Çalışır?

ESKİ — 1-Pass (Kör Tahmin)
  • FFmpeg dosyayı bir kere okur, anında normalize eder
  • Dosya başında ve sonunda farklı davranır (pumping)
  • Kısa şarkılarda aşırı gain, uzunlarda yetersiz
  • Şarkılar arası ~6 dB fark kalabilir
YENİ — 2-Pass (Hassas Ölçüm)
  • Pass 1: FFmpeg dosyayı okur, ses seviyesini ölçer (input_i, input_tp, input_lra, input_thresh)
  • Pass 2: Ölçülen değerlerle hassas normalizasyon uygular
  • Tüm şarkıda tutarlı davranır (pumping yok)
  • Şarkılar arası fark ~1 dB'ye düşer
v2 Filtre zinciri: loudnorm=I=-16:TP=-1.5:LRA=14:measured_I=X:measured_TP=X:measured_LRA=X:measured_thresh=X:linear=false,equalizer=f=100:t=q:w=1:g=1,alimiter=limit=0.95:attack=5:release=50
3 katmanlı filtre: loudnorm (ses eşitleme) → equalizer (hafif bass +1dB@100Hz) → alimiter (peak koruması, clipping önleme)

Test Sunucusu Doğrulama Sonuçları

237
Toplam HLS
237
4sn Segment
0
6sn Segment
149
MP3 128k
Kontrol Sonuç Durum
master.m3u8237/237Tamam
enc.bin237/237Tamam
enc.keyinfo237/237Tamam
high (ana playlist)237/237Tamam
ultralow (32k)237/237Tamam
low (64k)237/237Tamam
mid (128k)237/237Tamam
VOD tipi237/237Tamam
enc.keyinfo URI (/hls-key/)237/237Tamam
Segment süresi (≤4.01s)237/237Tamam
MP3 128k149/237Kısmi*

* 88 yeni şarkı henüz MP3 128k üretilmemiş — re-encode script bunu da yapacak.

Herkes İçin Özet

Sorun: Şarkılar arasında ses seviyesi farkı var. Bazıları patlıyor, bazıları çok kısık. Eski filtreler (lowpass, stereotools) hışırtı yapıyordu.

Çözüm: Her şarkıyı iki kere ölçüp (2-pass) aynı ses seviyesine getiren bir script. Yeni şarkı yüklemelerinde de otomatik 2-pass kullanılıyor. Hışırtı yapan filtreler kaldırıldı.

Süre: 3 paralel terminal ile ~15 saat (gece başlatılabilir).

Risk: Sıfır. Orijinal MP3'ler korunuyor. Sorun çıkarsa script'i durdur, eski dosyalar yerinde.

Teknik: Ne Değişiyor?

ESKİ — Sorunlu (v1)
  • Tek geçişli loudnorm (pumping artifact)
  • stereotools + lowpass + treble cut (hışırtı)
  • LRA=11 (aşırı agresif sıkıştırma)
  • Şarkılar arası ~6 dB fark
  • 17 şarkıda clipping (peak 0.0 dB)
YENİ — Temiz (v2)
  • İki geçişli loudnorm (hassas ölçüm)
  • Sadece bass EQ (+1dB@100Hz) + alimiter
  • LRA=14 (geniş dinamik aralık)
  • Şarkılar arası ~1 dB fark
  • Clipping imkansız (alimiter limit=0.95)

Her Şarkı İçin Üretilen Dosyalar

Orijinal (DOKUNULMAZ)
storage/tenant1001/app/public/muzibu/songs/song_xxxxx.mp3

Re-encode Edilenler (script bunları üretir)
storage/tenant1001/app/public/muzibu/hls/{song_id}/
├── playlist.m3u8          ← high kalite (orijinal bitrate, 4sn segment, VOD)
├── segment-*.ts           ← high segment'ler (AES-128 şifreli)
├── ultralow/playlist.m3u8 ← 32kbps, mono, 22050Hz
├── low/playlist.m3u8      ← 64kbps, mono, 22050Hz
├── mid/playlist.m3u8      ← 128kbps, stereo, 44100Hz
├── master.m3u8            ← 4 varyant referansı
├── enc.bin                ← DOKUNULMAZ (encryption key)
└── enc.keyinfo            ← DOKUNULMAZ (key URI + path + IV)

storage/tenant1001/app/public/muzibu/songs/
├── mp3_128/{song_id}.mp3  ← 128kbps MP3 fallback
└── mp3_64/{song_id}.mp3   ← 64kbps MP3 son çare fallback

Production Deployment — Adım Adım

0
Kod Değişikliklerini Deploy Et

Önce test sunucusundaki kod değişikliklerini production'a taşı:

cd /var/www/vhosts/muzibu.com/httpdocs/
git pull origin main

# Cache temizle
php artisan cache:clear
php artisan config:clear
php artisan view:clear
php artisan responsecache:clear
Bu adım şunları getirir:
  • ConvertToHLSJob.php — Yeni şarkı yüklemelerinde 2-pass loudnorm
  • HLSService.php — buildTwoPassFilter() metodu
  • AddHlsVariantsCommand.php — re-encode-high 2-pass
  • hls-streaming.blade.php — Güncel dokümantasyon
1
Script'i Production Sunucuya Kopyala

Script zaten git repo'da: re-encode-songs.php. git pull ile gelecek.

Gelmediyse, test sunucusundan kopyala:

scp mztest:/var/www/vhosts/mztest.muzibu.com/httpdocs/re-encode-songs.php \
    /var/www/vhosts/muzibu.com/httpdocs/re-encode-songs.php
Script'in raw hali: re-encode-songs.txt
2
Önce 1 Şarkı ile Dene
# Dry-run: Kaç şarkı var?
php re-encode-songs.php --dry-run

# Tek şarkıyla test et
php re-encode-songs.php --song=BİR_ŞARKI_ID
Beklenen çıktı:
[1/1] Song #XXXXX (ETA: ?dk)
   🔄 High re-encode... ✅
   🔄 ultralow... ✅
   🔄 low... ✅
   🔄 mid... ✅
   📋 master.m3u8 ✅
   🔄 MP3 128k... ✅
   🔄 MP3 64k... ✅

==================================================
✅ Tamamlandı: 1 başarılı, 0 başarısız (X.X dk)
3
Sonuçları Paylaş — Şu Soruları Yanıtla
PROMPT: Test Sonucu Paylaşım Şablonu
1. Script çalıştı mı? (Evet/Hayır)
   Varsa hata mesajını yapıştır.

2. Çıktıdaki tüm satırlar ✅ mi?
   ❌ olan varsa hangisi?

3. Süre ne kadar sürdü? (X.X dk)

4. Sunucu PHP versiyonu:
   php -v | head -1

5. FFmpeg versiyonu:
   ffmpeg -version | head -1

6. Toplam HLS'li şarkı sayısı:
   php re-encode-songs.php --dry-run
4
10 Şarkıyla Küçük Test
php re-encode-songs.php --limit=10

Hepsi ✅ ise devam. ❌ varsa DURMA, hata mesajını paylaş.

5
Tam Re-encode (3 Paralel Terminal)

Her terminali screen veya tmux içinde aç (SSH kopsa bile devam etsin):

# Terminal 1
screen -S encode1
php re-encode-songs.php --offset=0 --limit=11000

# Terminal 2
screen -S encode2
php re-encode-songs.php --offset=11000 --limit=11000

# Terminal 3
screen -S encode3
php re-encode-songs.php --offset=22000 --limit=11000

screen ipuçları:

Ctrl+A D → Oturumu arkada bırak (detach)

screen -r encode1 → Tekrar bağlan

screen -ls → Tüm oturumları listele

6
Doğrulama
HLSDIR="storage/tenant1001/app/public/muzibu/hls"
SONGDIR="storage/tenant1001/app/public/muzibu/songs"

echo "HLS klasör: $(ls -d $HLSDIR/*/ | wc -l)"
echo "ultralow: $(find $HLSDIR -maxdepth 2 -name ultralow -type d | wc -l)"
echo "low: $(find $HLSDIR -maxdepth 2 -name low -type d | wc -l)"
echo "mid: $(find $HLSDIR -maxdepth 2 -name mid -type d | wc -l)"
echo "master: $(find $HLSDIR -name master.m3u8 | wc -l)"
echo "mp3_128: $(ls $SONGDIR/mp3_128/*.mp3 2>/dev/null | wc -l)"
echo "mp3_64: $(ls $SONGDIR/mp3_64/*.mp3 2>/dev/null | wc -l)"

# Segment süresi doğrulama (rastgele 5 şarkı)
echo ""
echo "Segment süreleri:"
for dir in $(ls -d $HLSDIR/*/ | shuf | head -5); do
    ID=$(basename $dir)
    MAX=$(grep '#EXTINF:' "$dir/playlist.m3u8" | sed 's/#EXTINF:\([0-9.]*\),.*/\1/' | sort -n | tail -1)
    echo "  Song #$ID: ${MAX}s"
done
7
Temizlik + Cache
# Script'i sil (güvenlik)
rm re-encode-songs.php

# Cache temizle
php artisan cache:clear
php artisan view:clear
php artisan responsecache:clear

Script: re-encode-songs.php

Bağımsız script — Laravel kodlarını değiştirmeden tüm şarkıları yeniden encode eder.

Raw dosya: re-encode-songs.txt

Kullanım seçenekleri:
php re-encode-songs.php                          # Tüm şarkılar
php re-encode-songs.php --limit=100              # İlk 100 şarkı
php re-encode-songs.php --offset=100 --limit=100 # 101-200 arası
php re-encode-songs.php --dry-run                # Sadece say, encode yapma
php re-encode-songs.php --skip-high              # High re-encode atla
php re-encode-songs.php --only-mp3               # Sadece MP3 64k+128k üret
php re-encode-songs.php --song=34455             # Tek şarkı test

Tahmini Süreler

Şarkı Sayısı 1 Terminal 3 Paralel
1 şarkı ~2 dk
10 şarkı ~20 dk
1.000 şarkı ~33 saat ~11 saat
~32.000 şarkı ~44 saat ~15 saat

* 2-pass loudnorm olduğu için her şarkı ~2x FFmpeg çalıştırır (ölçüm + encode). Test sunucusu ölçümü: 1 şarkı = ~2 dk.

Sorun Giderme

❌ "enc.keyinfo yok" hatası

O şarkının HLS encryption dosyası eksik. --skip-high ile devam edebilirsin, sadece variant ve MP3 üretir.

❌ "MP3 bulunamadı" hatası

Orijinal MP3 dosyası silinmiş. O şarkıyı atlar, devam eder.

❌ FFmpeg hatası

ffmpeg -version ile versiyon kontrol et. Minimum 4.x gerekli. loudnorm filtresi 3.1+ gerektirir.

⚠️ "Ölçüm başarısız, tek geçiş" uyarısı

2-pass'ın Pass 1'i başarısız olmuş, tek geçiş fallback kullanıldı. Çoğunlukla sorun değil — FFmpeg 4.x'te nadiren olur. Çok sık çıkıyorsa FFmpeg versiyonunu kontrol et.

⚠️ Yarıda kaldı / SSH koptu

Sorun yok. Aynı komutu tekrar çalıştır — script mevcut dosyaları siler ve yeniden oluşturur. Kaldığı yerden devam etmek için --offset kullan.

🔙 Geri almak istiyorum

Orijinal MP3'ler yerinde. Eski HLS üzerine yazıldı. Eski haline döndürmek için eski filtreli script ile tekrar encode gerekir (ama genellikle gerekmez).

FFmpeg Standart Parametreler

High (Orijinal Kalite)
-map 0:a -c:a aac -b:a {orijinal_bitrate}k -profile:a aac_low -ar 48000 -ac 2
-af "loudnorm=I=-16:TP=-1.5:LRA=14:measured_*=...,equalizer=f=100:t=q:w=1:g=1,alimiter=limit=0.95:attack=5:release=50"
-hls_time 4 -hls_list_size 0 -hls_playlist_type vod -hls_key_info_file {enc.keyinfo}
Ultralow (32k)
-c:a aac -b:a 32k
-ar 22050 -ac 1 -vn
-hls_time 4 -hls_playlist_type vod
Low (64k)
-c:a aac -b:a 64k
-ar 22050 -ac 1 -vn
-hls_time 4 -hls_playlist_type vod
Mid (128k)
-c:a aac -b:a 128k
-ar 44100 -ac 2 -vn
-hls_time 4 -hls_playlist_type vod
MP3 128k (Fallback)
-b:a 128k -ar 44100 -ac 2 -map_metadata -1 -vn -y

Rollback Planı

Normalde rollback gerekmez. Orijinal MP3'ler aynen korunur. Re-encode sadece HLS dosyalarını (segment + playlist) yeniden oluşturur.

Kod rollback: git revert HEAD ve cache temizle.

Re-encode iptali: Script'i durdur (Ctrl+C). İşlenmiş şarkılar yeni filtrelerle, işlenmemişler eski filtrelerle kalır. Sorun yok — dinleyici fark etmez.

Tamamen eski haline dön: Eski filtrelerle (v1) re-encode script yaz ve tekrar çalıştır. Ama v2 filtreler daha iyi olduğu için bu genellikle gerekmez.

26 Şubat 2026 • Muzibu.com.tr