ffmpeg ile Telefonda Video Birleştirme: Farklı Oranlardaki Klipleri Tek Vloga Çevirmek
Anılog'un ana fikri şu: arkadaş grubu gün içinde belirlediği saatlerde birkaç saniyelik klip çekiyor, gün sonunda hepsi tek bir vloga dönüşüyor.
Kulağa basit geliyor, ama girdi tahmin edilebilir değil. Aynı gün içinde şunların hepsi olabiliyor:
- Dikey çekilmiş 2 saniyelik video
- Yatay çekilmiş 10 saniyelik video
- Tek kare fotoğraf
- Sesli klip ve sessiz klip
Hepsi tek bir 1920×1080 videoda, düzgün görünecek şekilde buluşmak zorunda. Üstelik bu iş telefonda yapılıyor — sunucuda ffmpeg çalıştırmıyorum.
Neden telefonda?
Sunucu tarafında birleştirmek daha kolay olurdu. Üç sebeple yapmadım:
- Maliyet. Paylaşımlı hostingte ffmpeg ya yok ya da bu iş için çok yavaş.
- Depolama. Vlog dosyası sunucuya hiç yüklenmiyor. Bir uygulamada en büyük dosya vlog'sa, onu hiç yüklememek en büyük tasarruf.
- Gizlilik. "Günlük vlog cihazınızdan çıkmaz" cümlesini yazabilmek, gerçekten öyle olduğu için değerli.
Flutter tarafında ffmpeg_kit_flutter_new paketini kullandım.
Adım 1: Her klibi ortak bir çerçeveye oturtmak
Farklı oranlardaki görüntüleri birleştirmenin klasik yolu, kısa kenarları siyah bantla doldurmak. Sonuç kötü görünüyor.
Bunun yerine bulanık dolgu kullandım: aynı görüntünün büyütülüp bulanıklaştırılmış hâli arka plan oluyor, gerçek görüntü ortasına oturuyor. Instagram ve TikTok'ta gördüğünüz efekt.
Filtre zinciri şöyle:
[0:v]split=2[zemin][on];
[zemin]scale=1920:1080:force_original_aspect_ratio=increase,
crop=1920:1080,
gblur=sigma=28,
eq=brightness=-0.18[bulanik];
[on]scale=1920:1080:force_original_aspect_ratio=decrease[sigan];
[bulanik][sigan]overlay=(W-w)/2:(H-h)/2,
fps=30,setsar=1,
format=yuv420p[v]Okunuşu:
split=2— aynı girdiyi iki kola ayırıyor.- Zemin kolu
increaseile çerçeveyi taşacak kadar büyüyor,cropile kesiliyor. Böylece boşluk kalmıyor. gblur=sigma=28bulanıklaştırıyor,eq=brightness=-0.18karartıyor. Karartma olmadan arka plan öndeki görüntüyle yarışıyor.- Ön kol
decreaseile çerçeveye sığacak kadar küçülüyor. overlayortalıyor.
setsar=1 şart. Bazı telefonlar kare olmayan piksel oranıyla kayıt yapıyor; bunu sıfırlamazsanız birleştirmede görüntü yatay olarak eziliyor.
Fotoğraflar aynı zincirden geçiyor, tek fark girişte -loop 1 -t <saniye> olması.
Adım 2: Yazıları geçirmek — ve Türkçe karakterler
Her klibin üstünde çeken kişinin adı, çekim saati ve varsa açıklaması var. drawtext filtresiyle basılıyor.
Burada erken öğrendiğim bir ders var: metni doğrudan filtre metnine yazmayın.
drawtext=text='öğle molası uzadı' ← kırılganFiltre zinciri bir dizgi; içindeki : ' % \ karakterlerinin hepsi kaçış istiyor. Kullanıcının yazdığı bir açıklamada bunların hepsi olabilir. Bir kesme işareti tüm komutu bozuyor.
Çözüm textfile:
drawtext=fontfile=/.../Manrope.ttf:textfile=/.../aciklama.txt:x=76:y=h-th-64Metin geçici bir dosyaya UTF-8 olarak yazılıyor, ffmpeg oradan okuyor. Kaçış sorunu tamamen ortadan kalkıyor.
İkinci nokta: fontfile vermezseniz ffmpeg sistemin varsayılan fontunu kullanıyor ve Türkçe karakterler kutu olarak çıkıyor. Uygulamanın kendi fontunu (Manrope, Fredoka) uygulama paketinden geçici klasöre kopyalayıp yolunu veriyorum.
Adım 3: Klipleri uç uca eklemek
Tüm klipler aynı çözünürlük, aynı kare hızı ve aynı ses formatına normalize edildikten sonra birleştirme ucuz:
-f concat -safe 0 -i liste.txt -c copy cikti.mp4concat demuxer, -c copy ile yeniden kodlama yapmadan çalışıyor — saniyeler sürüyor. Ama yalnızca girdiler gerçekten aynıysa. Tek bir klip farklı kare hızındaysa ses ile görüntü kayıyor. Normalizasyon adımını atlamayın.
Alternatif olan concat filtresi her şeyi yeniden kodluyor ve telefonda çok daha yavaş.
Adım 4: Müzik eklemek
Kullanıcı dosyadan bir müzik seçebiliyor, kliplerin sesiyle müziğin dengesini ayrı ayrı ayarlayabiliyor. Üç sorun var:
Müzik vlogtan kısa olabilir. Girişte döngüye alınıyor:
-stream_loop -1 -i muzik.mp3Ani başlama ve bitme kulağı rahatsız ediyor. Girişte 1 saniye, çıkışta 2 saniye yumuşama:
afade=t=in:st=0:d=1,
afade=t=out:st=<bitiş-2>:d=2İki ses kanalı birleşmeli:
[k][m]amix=inputs=2:duration=first:dropout_transition=0[a]duration=first önemli: müzik döngüde olduğu için süresi sonsuz sayılıyor; first demezseniz çıktı bitmiyor. dropout_transition=0 ise bir kanal sustuğunda diğerinin ani yükselmesini engelliyor.
Adım 5: Sıkıştırma — ve ölçmek
Depolama sınırlı olduğu için yüklenen klipler sıkıştırılıyor. Buradaki soru şu: kalite ne kadar düşüyor?
"Gözüme iyi göründü" bir ölçüm değil. ffmpeg'in kendi araçlarıyla ölçtüm:
ffmpeg -i orijinal.mp4 -i sikistirilmis.mp4 \
-lavfi "psnr;[0:v][1:v]ssim" -f null -CRF 23 ve 720p'ye ölçekleme ile gerçek bir kullanıcı klibinde çıkan sonuç:
| Ölçüt | Sonuç | Yorum |
|---|---|---|
| PSNR | 43,35 dB | 40 dB üstü genelde "gözle ayırt edilemez" kabul edilir |
| SSIM | 0,974 | 1,0 mükemmel benzerlik |
Bu sayılar kararı kolaylaştırdı. Dosya boyutu ciddi biçimde düştü, kalite kaybı ölçülebilir biçimde ihmal edilebilir seviyede kaldı.
Öneri: sıkıştırma ayarını "iyi görünüyor" diyerek seçmeyin. İki komut, iki sayı, ve elinizde savunulabilir bir karar oluyor.
Kaçınılması gereken beş şey
- Siyah bantla doldurmak. Bulanık dolgu neredeyse aynı maliyette ve çok daha iyi görünüyor.
drawtext=text=ile kullanıcı metni basmak.textfilekullanın.fontfilevermemek. Türkçe karakterler kutuya dönüyor.- Normalize etmeden
concat -c copyyapmak. Ses görüntüden kayıyor. setsar=1yazmamak. Bazı cihazlarda görüntü eziliyor.
Anılog'un mimarisinin tamamı ve bu kararların bağlamı ayrı bir yazıda.
İlgili Yazılar
Flutter'da Kamerayı Kullanıcıya Bırakmak: Yön Zorlamadan Video Çekmek
Flutter camera paketinde önizlemenin ters dönmesi, kayıt başlarken donma ve bildirim merkezi açılınca kameranın kilitlenmesi. Anılog'u geliştirirken çözdüğüm üç somut hatayı ve yön zorlamadan çalışan kamera ekranının kodunu anlatıyorum.
Firebase Auth + Kendi PHP Sunucun: Composer Olmadan Token Doğrulama ve Hesap Silme
Kimlik Firebase'de, veri kendi sunucunuzda. Peki PHP tarafı gelen jetona nasıl güvenecek? Paylaşımlı hostingte composer olmadan RS256 doğrulaması ve mağazaların zorunlu tuttuğu hesap silme akışının tam kurulumu.
Dart'ta Interface ve Implements: Sınıflara Ekstra Özellik
Abstract class'lar ve implements anahtar kelimesi ile sınıflarımıza nasıl ekstra özellikler ekleyebileceğimizi öğreniyorum.