Video donuyor, ses devam ediyor: libvlc'nin hâlâ Playing dediği bir takılmayı saptamak
Donmuş bir görüntü hata durumu değildir. Motor Playing bildirmeye devam eder ve değişen tek şey, sıfıra inen bir sayaç deltasıdır.
displayed +25 read +50,000 demux +48,000 audio +40 engine state: Playing
displayed +0 read +50,000 demux +48,000 audio +40 engine state: Playing
Arka arkaya gelen iki birer saniyelik örnek. Değişen tek bir sayı. Baytlar hâlâ aynı hızda geliyor, demuxer hâlâ onları tüketiyor, ses arabellekleri hâlâ ses kartına ulaşıyor ve motor iki satırda da Playing bildiriyor; kanalı açık bıraktığınız sürece de bildirmeye devam edecek. Ekranda duran şey, video çıkışına ulaşan son kare. Bu sayılar gerçek bir kanaldan alınmış bir kayıt değil, test düzeneğimizin algılayıcıya verdiği sabitler: 4 Mbit/s'lik bir yayın akışı saniyede yaklaşık 500.000 bayt okur, yani bu sabitlerden bir büyüklük mertebesi fazla; okuma ile demux sayıları birbirini, sürekli yüzde 4'lük bir farktan çok daha yakından izler. Gerçeğe taşınan şey şekil. Çiftin içinde hata sayılacak hiçbir şey yok, ardından bir hata olayı gelmiyor ve bir algılayıcının eline geçen kanıtın tamamı o dört delta.
Aşağıdaki her şey, Windows masaüstü uygulamamızda libvlc 3.0.21 ve LibVLCSharp 3.9.7 üzerinde ölçüldü. İstatistik API'si libvlc ana sürümleri arasında değişti; dolayısıyla sayaç savlarını 3.x hakkında savlar olarak okuyun.
libvlc, görüntü donmuşken de Playing bildiriyor
Motor durumu, insanların ondan okuduğundan daha dar bir soruyu yanıtlıyor. Playing, oynatma iş parçacığının durdurulmadığı, duraklatılmadığı ya da bitmediği anlamına geliyor. Son dört saniye içinde ekrana bir kare ulaştığı savını taşımıyor; ulaşan karenin içinde bir şey olduğu savını da taşımıyor.
Watchdog spesifikasyonumuz, bütün alt sistemin üzerine oturduğu öncülü şöyle yazıyor: libvlc hem bir donma hem de siyah bir yayın boyunca süresiz olarak Playing durumunda kalır ve EncounteredError sert açılış ile protokol hatalarına ayrılmıştır; dolayısıyla izleyicilerin gerçekten bildirdiği iki hata biçiminde neredeyse hiç tetiklenmez. Bu cümle, motorun belgelerinden alınmış bir satır değil, bizim motoru okuyuşumuz; aşağıdaki kuralların hiçbirinin motora nasıl olduğunu sormamasının sebebi de bu.
Bunun gözlenen sonucu, kurmak zorunda kaldığımız taksonominin büyüklüğü. Dokuz sınıf tanımlı: erişim katmanında hiç bayt gelmemesi, canlı bir kanalda sessiz bir akış sonu, motorun kendi hatası, demuxer hiçbir şey üretmezken baytların gelmesi, ses canlıyken videonun takılması, veri akarken ikisinin birden takılması, sağlıklı bir işlem hattında siyah kareler, duran bir girdi saati ve kayan bir pencere içinde tekrarlayan yeniden arabelleğe alma.
Bu dokuzun üçü motor olaylarına biniyor. Biri motorun kendi hatası. Diğer ikisi hata bile olmayan sıradan olaylar: kimsenin durdurmadığı bir kanalda akış sonu ya da sessiz bir duruş, bir de yeniden arabelleğe alma döngüsüne saydığımız, önbellek düzeyinde bir bildirim. Altısı sayaçlardan çıkarsanıyor; gerçi aralarındaki duran saat, motorun saat olayına da yaslanıyor. Üstelik dokuz, sistemin söyleyebileceğini abartıyor; çünkü duran saat sınıfı hiçbir yerde ilan edilmiyor. Var olma sebebi başka bir hükmü desteklemek ve kendine ait bir taksonomi kodu yok.
Arıza kartı eskiden bunların hepsini çöpe atıp her durum için tek bir cümle gösteriyordu; yani kaynağı bağlantıyı reddeden izleyici ile cihazı videoyu çözemeyen izleyici tıpatıp aynı sözleri okuyordu. Artık her sınıf bir koda eşleniyor ve aynı sınıf, ilk karenin iki yakasında farklı şeyler anlattığı için eşleme ikisini birden taşıyor. Bağlantı için tanınan sürenin içinde hiç bayt gelmemesi, hiç başlamamış bir yayın akışı demek. Onuncu dakikada gelen tıpatıp aynı takılma ise durmuş bir akış demek.
Video donması algılama, sabit bir sırayla okunan dört sayaç deltasından ibaret
Bir örnek on bir alandan oluşuyor: bir monotonik zaman damgası ve motordan tek geçişte okunan on birikimli sayaç. Onun sekizinin farkı bir önceki örneğe göre alınıyor. Diğer ikisinin, yani düşürülen geç karelerle düşürülen ses arabelleklerinin farkını hiçbir şey almıyor; bir kural isterse diye toplanmışlardı ve hiçbir kural hiç istemedi. Hükümleri dört delta taşıyor:
- Erişim katmanında okunan baytlar. Sıfır, ağdan hiçbir şeyin gelmediği anlamına geliyor.
- Demuxer'ın tükettiği baytlar. Okuma sıfırdan farklıyken demux sıfırsa, gelen baytların içinde program yok demektir.
- Video çıkışında gösterilen kareler. Birincil video canlılık sinyali bu ve bir sonraki bölüm, ona ilk bakışta neden inanılamayacağını anlatıyor.
- Ses çıkışında çalınan ses arabellekleri. Video donmasını tam donmadan ayıran şey bu.
Yetkisi olan tek destek sayacı, kod çözülen video: gösterilen kare sayacı kullanılamaz çıktığında onun yerine geçiyor. Kod çözülen sesin buna karşılık gelen bir rolü yok. Bir ses akışının var olduğunu kanıtlamak için bir kez okunuyor. Geriye kalan çift bozuk birimler ile süreksizlikler; ikisinden birindeki sıçrama, bir süreliğine kısaltılmış donma pencerelerini kuruyor.
Sıra, tek tek eşiklerin hepsinden daha önemli ve kod bunun sebebini yerinde yazıyor:
// F5 / F6 presentation freezes. Suppressed while demux starvation is suspected: starvation
// stalls presentation too, and F4's longer window owns the classification (its ladder entry
// differs). F1 needs no such guard; it shares the shorter window and is checked first.
(Yorumun özeti: demux açlığından şüphelenilirken sunum donmaları bastırılıyor; sınıflandırma, daha uzun pencereyi tutan kuralın işi.)
Önce erişim, sonra demuxer, en son sunum sınanıyor. Ölü bir erişim katmanı, altındaki her şeyi aç bırakır; yani baytlar durduktan dört saniye sonra duran bir görüntü teşhis değil, belirtidir ve onu donma diye bildirmek kurtarma merdivenini yanlış basamaktan içeri sokar. Şu anki pencerelerimiz dört saniye sıfır bayt, altı saniye aç kalmış demuxer, dört saniye takılmış sunum. Bunlar ayar değerleri ve biri onları bir dahaki ölçüşünde değişecekler; anlamı taşıyan şey sıralama.
Şüphe bayrak olarak değil, an olarak saklanıyor. Dört etiketin her biri kendi koşulunun başladığı zaman damgasını tutuyor ve hüküm, yaşı penceresini geçmiş bir etiket demek. Tek bir sağlıklı tick etiketi null'a geri alıyor ve yaş yok oluyor; yani pencere, her tick tutulan bir kayıt değil, bir çıkarma işlemi. Bozulma sıçraması, şekli doğrulayan istisna: başlangıç yerine bir bitiş anı saklıyor, hiçbir şey onu erken temizlemiyor ve hiçbir hüküm yaşını okumuyor.
Eşikler evrensel görünmeye başlamadan önce bir kapsam notu. Bütün bunlar ham MPEG-TS üzerinde kalibre edildi. Kaynak türünü ts ya da hls olarak taşıyoruz ama yalnızca telemetri için ve henüz kimsenin yazmadığı, türe özel eşikler için. HLS'te uyarlanır demuxer kendi alt getirmelerini yapıyor; yani üst düzeydeki erişim ve demux ayrımı bu bölümün söylediği anlama gelmiyor ve sürüme çıkardığımız hiçbir eşik HLS'e özgü değil.
Kare sayacı neden sağlıklı bir kanalın sürekli donduğunu söylüyor?
Bazı video çıkışı ve donanım kod çözme yolları, gösterilen kare sayısını eksik bildiriyor ve hangisinin üzerinde olduğunuzu soracak bir yol yok. Kendi kod yorumumuz bunun suçunu erişim modülüne atıyor, ki bu doğru olamaz: erişim katmanı demux ve kod çözmenin yukarısında durur ve video çıkışı çekirdeğinin artırdığı sayaca hiç dokunmaz. Yorum yanlış, anlattığı davranış gerçek.
Bu yüzden bir yayın akışı görüntü vermeye başladıktan sonraki ilk otuz saniye boyunca makine hüküm vermek yerine izliyor. Pencerenin sonunda beş dal üzerinden üç konumdan birine bağlanıyor: gösterilen kare sayacı ilerledi ve güveniliyor; gösterilen kare sayacı ölü ama kod çözülen video ilerledi ve yedek sinyal oluyor; ya da girdi saati ilerlerken ve bir video çıkışı varken bütün video sayaçları ölü, yani yol eksik bildiriyor ve donma kuralları o oturum boyunca kapanıyor. Dördüncü dal yanlış etiketlenmiş bir kanalı yakalıyor: video tarafında hiçbir şey kıpırdamamış, hiç video çıkışı belirmemiş ama ses çalıyor; devreyi ses canlılığı devralıyor. Beşinci dal ise öntanımlı olarak sayaca güveniyor.
Bütün bunların üstünde tek bir koruma duruyor. Bu oturumda bir video sayacı en az bir kez ilerlemeden hiçbir donma hükmü mümkün değil; çünkü hiç kıpırdamamış bir sayaç ile eksik bildiren bir sayaç birbirinden ayırt edilemez ve baştan gerçekten ölü olan durum, farklı kanıtları ve farklı bir merdiven girişi olan açılış sınıflandırıcısına ait. Ses çalarken, saat ilerlerken ve bir video çıkışı ayaktayken bütün video sayaçlarının düz kaldığı kırk beş birer saniyelik tick, hiçbir şey ilan etmiyor.
Kalibrasyona şeklini veren uç durum, düşük kare hızlı tanıtım kartı: saniyede yaklaşık yarım kare güncellenen bir kanal tanıtım ekranı ya da bir radyo görseli. Saniyede bir örnekleyen bir düzenekte o kanal, her iki tick'ten birini sıfır gösterilen kareyle geçiriyor, yani tam olarak bir donmanın imzasıyla. Kalibrasyon hızı saptıyor ve donma penceresini dört saniyeden on beş saniyeye genişletiyor; sezgiye ters gelen yarı da bu genişletme: insanın içinden, gösterecek daha az şeyi olan bir kanalda daha erken hüküm vermek geliyor ve bu, elinizdeki her tanıtım kartında yanlış bir hüküm üretiyor. Tanıtım kartının tamamen durması, geniş pencerede yine yakalanıyor.
Hiçbir kuralın tüketemeyeceği bir şüpheyi kurmanın bir bedeli var ve biz bu bedeli ödedik. Sağlıklı videonun yanındaki bir ses takılması, eskiden bir video kanalında şüphe uyandırıyordu; oysa orada hiçbir hüküm onu okuyamaz. Böylece oturum ömrü boyunca o durumda kalıyordu: iki kat hızlı örnekleyerek ve kurtarma defterini temizleyen kararlılık sinyalini hiç yaymadan. O oturumdaki sonraki her arıza, denemeleri çoktan tükenmiş bir merdiven devralıp doğrudan vazgeçme kartına gitti. Hükme dönüşemeyen bir şüphe temkinli değildir. Sızıntıdır.
Duraklatma tıpatıp donmaya benziyor
Kullanıcı duraklattığında örnekteki her sayaç duruyor, üstelik tam olarak bir tam donmanın ürettiği desende.
İkisini sayılardan ayırt etmenin yolu yok; bu yüzden duraklatmaya hiç süre konmuyor: hükümleri süresiz maskeliyor ve bir duraklatmanın arkasındaki on dakikalık donmuş sayaçlar hiçbir şey ilan etmiyor. Sarma işlemi ise beş saniyelik sınırlı bir maske alıyor, çünkü bir sarma ya sonuçlanır ya sonuçlanmaz. Arabelleğe alma, sunum hükümlerini askıya alıyor ama erişim kuralını kurulu bırakıyor; gerekçe şu: arabelleğe alma sırasında sıfır bayt, yavaş bir bağlantı değil ölü bir kaynak demektir, çünkü yavaş bir bağlantıda baytların geldiği yine görünür.
Aritmetiğin ısırdığı diğer yer yeniden açmalar. Bayt sayaçları 32 bit ve tek bir medyaya bağlı; bu yüzden yeni medya uygulandığında sıfırdan başlıyorlar ve yeterince uzun bir oturumda başa sarıyorlar. Her iki durumda da delta negatif dönüyor; altı canlılık sayacından herhangi birindeki negatif bir delta da işlem hattının tamamının takıldığını bildirmek yerine referansı sıfırlıyor ve o tick için değerlendirmeyi atlıyor.
Siyah kare algılamak piksellere bakmayı gerektiriyor
Siyah bir yayın kod çözer, gösterir, ses çalar ve baytları olağan hızda çeker. Buraya kadarki her kural sağlıklı bir yayın akışı görüyor. Algılayıcıda piksellere bakmak zorunda olan tek yol bu ve bunun için video çıkışından 96'ya 54 çözünürlükte yakalanmış gerçek bir kare gerekiyor.
Tek bir luma eşiği yetmiyor ve sebebi içerik. Hüküm iki eksen ve tekrar istiyor:
- Siyah piksel oranının 0.98 ve üzerinde olması; bir piksel luma 24 ve altındayken siyah sayılıyor.
- Luma standart sapmasının 4.0 ve altında olması; karanlık ama yapılı bir sahneyi kurtaran şey bu.
- Dört ardışık siyah örnek; bir karartma geçişinin sağlayamayacağı şey de bu.
İçerik kaynaklı iki hata da tartışmayla değil testle sabitlenmiş. Çoğunlukla siyaha yakın olan, aralarına ay ışığıyla aydınlanmış yapılar serpiştirilmiş bir gece sahnesi, iki ekseni birden geçemiyor. Letterbox bantlı içerik yarıdan az siyah çıkıyor, çünkü bantlar geniş bir en boy oranında bile karenin azınlığı.
Karartma savının atlamamam gereken bir dalı var. Bir kurtarma doğrulandıktan sonraki on saniye boyunca gereken dizi, dört örnekten iki örneğe, yani yapılandırılmış tabanda yaklaşık üç saniyeye düşüyor, pratikte daha da uzun; yavaş bir karartma bunu tutabilir. Bu kasıtlı bir ödünleşme: kaynağı az önce mahkûm eden kanıt hâlâ ayakta, dolayısıyla bozuk dönen bir kaynak daha hızlı yeniden mahkûm ediliyor ve bunu satın almak için ödediğimiz şey karartma koruması. 1500 ms'lik değer de bir aralık değil, bir taban. Yoklama bunun üzerine yeniden kuruluyor ve gelen ilk mesajda tetikleniyor; yani gerçekleşen boşluk daha uzun: aşağıdaki çalıştırmada yedi saniye, sonra dokuz saniye.
Yoklamanın ne zaman çalıştığı, görünür bir bedeli olan bir karar ve dürüst tarif, adın çağrıştırdığından daha dar. Bir yayın akışı ilk kez görüntü verdikten hemen sonra tarama olarak bir kez çalışıyor, bir de şüpheyi bir video takılması uyandırdığında şüpheye geçişte çalışıyor. Açılış taraması kapandıktan sonra, oturum sağlıklı kaldığı sürece bir daha çalışmıyor. Dolayısıyla başka türlü sağlıklı bir akışta oynatma ortasında oluşan siyahlık hiçbir hüküm üretmiyor; bu yeniden üretilebilir bir boşluk ve onu düzeltmemeyi seçtik: yakalamanın bedeli, izleyen kişinin zaten anında gördüğü bir şeyi saptamak uğruna her yayın akışının her oturumunda bir anlık görüntü almak ve kanaldan çıkıp geri dönmek zaten taramayı yeniden çalıştırıyor.
Kanıtla kapatamadığım ikinci bir kısıt. Yakalama, motorun kendi anlık görüntü çağrısı üzerinden gidiyor ve donanımla kod çözülmüş bir yüzeyden geri okuma, bir oynatıcının ekranda ne olduğuyla ilgisi olmayan sebeplerle boş kare geri vermesinin çok bilinen yerlerinden biri. Hiç gelmeyen anlık görüntüyü karşılıyoruz: takılmış bir video çıkışının kanıtı sayılıyor, bir sayaç hükmünü destekliyor ama tek başına hiçbir zaman hüküm vermiyor. Gelen ama yanlışlıkla siyah olanı karşılamıyoruz ve bunun bizim yolumuzda ne sıklıkta olduğuna dair elimde ölçüm yok.
En rahatsız olduğum kısım, yoklamanın ölçüp sonra attığı şey. Her örnekte luma düzleminin 64 bitlik bir ortalama hash'i ile bir ortalama luma hesaplanıyor, ikisi de mesajın içinde yol alıyor ve kural motoru hiçbirini okumuyor. Kare sayacı güvenilmez diye kalibre edilmiş bir kanalda, parlak ve durağan bir karede donmuş bir yayın akışı, aralıklı örnekler arasındaki iki eşit hash'in yakalayacağı şeydir. Biz onları karşılaştırmıyoruz; bu durumun kuralı hak edecek kadar yaygın olup olmadığı ise cevaplayamadığımız bir soru ve sebebine son iki bölümde geliyoruz. Hash'le ilgili bir ayrıntı beni saklayacak kadar şaşırttı: düz bir kare, bütün bitleri kurulu bir hash veriyor, 64 bitin hepsi birden; çünkü her hücrenin ortalaması tam olarak global ortalamada duruyor ve karşılaştırma büyük ya da eşit. Düz beyaz ile düz siyah aynı hash.
Buradaki en kötü hata, hiç yaşanmayan bir donmaydı
Oynatıcının istatistiklerini okumak güvenli bir çağrı değil. Bu işin her satırı; kullanıcı kanal değiştirdiğinde, sayfadan çıktığında ya da bir swapchain yeniden kurulumu oynatıcıyı emekliye ayırdığında motorun serbest bıraktığı native tutamaçların içine giriyor ve örnekleyici kendi görevinde çalışıyor. Bu yarışı kaybetmek, bir catch bloğunun soğurabileceği bir şey doğurmuyor: libvlc bir erişim ihlaliyle hata veriyor ve süreç, logda hiçbir şey bırakmadan yok oluyor. Bu yüzden örnekleyici, oynatıcı havuzunun kullandığı kapının arkasında çalışıyor.
O kapının içinde, tek başına bakıldığında doğru görünen bir önbellekleme kararı, bu alt sistemin sürüme çıkardığı en kötü hatayı üretti. Örnekleyici, medya sarmalayıcısını her tick'te almak yerine elinde tutuyordu; çünkü saniyede bir almak israf gibi görünmüştü. Bir LibVLCSharp medya sarmalayıcısı kendi native referansını alıyor; bu yüzden motor, altındaki medyayı bir havuz iş parçacığında değiştirdikten sonra da önbellekteki sarmalayıcı geçerli kaldı. Cevap vermeye devam etti. Hiç istisna fırlatmadı. Ölü medyanın sayaçlarını döndürdü; o sayaçlar da kıpırdamıyor, ki bu makinedeki her kurala göre tam bir bayt takılması demek.
Belirti, sağlıklı bir kanalın ölü ağ diye bildirilmesiydi. Bedeli, çalışan bir yayın akışından başka bir kaynağa geçilmesi oldu; 2026-07-24 tarihinde saat 23:24'te bir saha kabul çalışması sırasında canlı olarak gözlendi. Düzeltmenin başında şimdi duran yorum bunu açıkça söylüyor:
// Acquire and dispose the wrapper on every tick. A cached wrapper holds its own native
// retain, so after a pool-thread media swap it would keep returning the DEAD media's
// frozen counters without ever throwing, which reads as a net stall on a healthy stream
// (observed live: [...] 2026-07-24 23:24). One retain/release per second is negligible.
(Yorumun özeti: sarmalayıcı her tick'te alınıp bırakılıyor; saniyede bir alıp bırakmanın maliyeti ihmal edilebilir.)
Bayat girdi okuyan bir algılayıcı sessizce çuvallamaz. Kusursuz oynayan bir kanal hakkında aynı hükmü aynı güvenle üretir, sonra da ona göre davranır. Bir watchdog'taki diğer her hata size yakalayamadığınız bir takılmaya mal olur. Bu hata ise uydurduğunuz bir takılmaya mal oluyor ve izleyici kurtarmayı gözüyle görüyor.
On iki saniyelik bir donma gerçekte ne yayıyor
Aşağıda, mevcut test düzeneği üzerinde sürüme çıkmış kural motorundan geçirilmiş, betiklenmiş bir oturum var: on iki birer saniyelik tick. Elle takip edilmedi, gerçekten çalıştırıldı ve sürücü sonrasında silindi. Sayıları düzenek verisi olarak okuyun. Her anlık görüntü isteği, gerçek bir host'un yapacağı gibi 300 ms sonra yanıtlandı; çünkü anlık görüntü bütçesi bir saniye.
Oturum açılıyor, Playing bildiriyor ve bir video çıkışı bildiriyor. Sonra:
| t | dRead | dDemux | dDisplayed | dAudio | sonraki durum | yayılan etkiler |
|---|---|---|---|---|---|---|
| 1 s | +50.000 | +48.000 | +25 | +40 | Starting | yok |
| 2 s | +50.000 | +48.000 | +25 | +40 | Healthy | StateChanged, SetSampleRate 1 Hz, RequestSnapshot |
| 3 s | +50.000 | +48.000 | +25 | +40 | Healthy | yok |
| 4 s | +50.000 | +48.000 | +25 | +40 | Healthy | yok |
| 5 s | +50.000 | +48.000 | +25 | +40 | Healthy | yok |
| 6 s | +50.000 | +48.000 | +25 | +40 | Healthy | yok |
| 7 s | +50.000 | +48.000 | +0 | +40 | Suspect | StateChanged, SetSampleRate 2 Hz, RequestSnapshot |
| 8 s | +50.000 | +48.000 | +0 | +40 | Suspect | yok |
| 9 s | +50.000 | +48.000 | +0 | +40 | Suspect | RequestSnapshot |
| 10 s | +50.000 | +48.000 | +0 | +40 | Suspect | yok |
| 11 s | +50.000 | +48.000 | +0 | +40 | Recovering | StateChanged, FaultDeclared VideoFreeze, yaş 4000 ms |
| 12 s | +50.000 | +48.000 | +0 | +40 | Recovering | yok |
Baştaki 1. tick hiçbir şey üretmiyor, çünkü tek bir örnek delta değildir; o örnek referans oluyor. 2. tick değerlendirilen ilk çift; sunumun ilerlediği kanıtlanıyor ve makine Starting'den çıkıyor. Ayrıca zaten kullandığı bir örnekleme hızını yeniden bildiriyor, ki bu gereksiz ve zararsız. O satırdaki anlık görüntü isteği açılış taraması; host isteği gerçek içerikle yanıtladı: yüzde 6 siyah piksel, 41 standart sapma. Tarama da böylece kapandı.
3 ile 6 arasındaki tick'ler hiçbir şey yaymıyor. Amaçlanan kararlı durum bu: sağlıklı bir yayın akışı hiçbir etki üretmez, yani boş bir log başarı durumudur.
Görüntünün durduğu yer 7. tick. Video takılma etiketine 7 s damgası vuruluyor, makine Suspect'e geçiyor, örnekleyici 2 Hz'e çıkıyor ve yoklama kurulup bir kare istiyor. Hiçbir şey ilan edilmiyor, çünkü yaşı sıfır olan bir etiket hüküm değildir. 8. ve 10. tick'ler pencerenin işlemesi, 9. tick ise yoklamanın kendi temposunda yeniden örneklemesi: ayarlanan 1500 ms yerine son istekten iki saniye sonra, çünkü yoklama, zamanı geçtikten sonraki ilk mesajda tetikleniyor. Baytlar ve ses bunların hiçbirinde sekmiyor: bu oturum, bir kural dışında her kurala kusursuz görünüyor.
Hüküm 11. tick'te. Video takılma etiketi dört saniyeye ulaşıyor, demuxer şüphe altında değil, ses takılma boyunca ilerledi ve hâlâ ilerliyor; yani bu tam donma değil video donması ve yürütücüye bildirilen anomali yaşı 4000 ms. Tıpatıp aynı betiği, sesin de 7. tick'te durduğu hâliyle çalıştırın; aynı satır tam donma ilan ediyor. Bir kez daha, anlık görüntü içerik döndürmek yerine zaman aşımına uğrayacak şekilde çalıştırın; hüküm değişmiyor ama gerekçe dizesi video stalled while audio keeps playing (snapshot timeout observed) oluyor, yani ses çalmaya devam ederken video takıldı ve anlık görüntü zaman aşımı gözlendi. Takılmış çıkış destekledi, hükmü o vermedi.
Sondaki 12. tick hiçbir şey yaymıyor, çünkü makine devri teslim etti. Bu kodun etrafındaki Playback paketi 108 testten oluşuyor ve bu satırlar yazılırken hepsi geçiyor.
Bir hüküm beş basamak satın alıyor, biri de yapılmamış
Hüküm bir cevap değil, bir merdivenin girdisi. Beş basamak var: yalnızca tam donmalara verilen bir duraklat ve oynat dürtmesi; aynı URL'nin tamamen yeniden uygulanması; bir sonraki adaya geçiş; oynatıcının kendisinin yeniden kurulması; ve vazgeçme. Kanal açıldıktan sonraki on beş saniye içinde çıkan bir arıza, yeniden uygulamadan değil geçişten giriyor; çünkü kendini hiç kanıtlamamış bir kaynak, az önce başarısız olan URL'yi yeniden denemeyi hak etmiyor. Olgunlaşmış oturumlarda yeniden uygulama başta kalıyor, böylece onuncu dakikadaki bir hıçkırık izleyiciye kaynak değişikliğine mal olmuyor. Tırmanmadan önce tam olarak bir yeniden başlatma denemesi var, çünkü saha verisi ikincisinin hiç işe yaramadığını gösterdi.
Yeniden kurma basamağı şu an için dürüst bir kurgu. Host tarafında karşılığı yok, sürüme çıkan ayarlarda kapalı ve ona yönlendiren tek koşul, yani zaman aşımına uğrayıp native oturumu takılı bırakan bir durdurma, şu anda ön plandaki merdiveni bitirip vazgeçmeye düşüyor. Bu temkinli ve doğru; aynı zamanda bir boşluk ve kod bunu, olayın geçtiği yerde söylüyor.
Denemeler arasındaki geri çekilme üstel ve ilk deneme bedava: taban çarpı ikinin deneme indisi kuvveti eksi bir, tavanla sınırlanıyor, sonra iki yöne de yüzde yirmiye kadar jitter uygulanıyor. Pratikte: hemen tetikle, sonra bir saniye, üç, yedi ve tavana kadar böyle devam et. Jitter tavandan önce değil sonra çarpıyor; yani gerçek üst sınır, tavanın ima ettiği otuz saniye değil otuz altı saniye. Küçük bir şey ama gecenin 3'ünde bir grafiği yanlış gösteren cinsten küçük bir şey.
Pikseller, onları yakalayan makinede kalıyor
Yoklama, uygulamanın kendi yerel veri klasörüne dönüşümlü iki PNG dosyası yazıyor ve onların üzerine yerinde yazıyor. Hiçbir şey onları yüklemiyor, hiçbir şey hiçbir yere göndermiyor; kare, iki sayıya ve bir hash'e indirgenecek kadar yaşıyor, sonra bir sonrakinin altında kalıyor. Arıza hükümleri aynı makinedeki yerel loga gidiyor ve orada bir kanal, hiçbir zaman URL ile değil, bir SHA-256'nın ilk on altı onaltılık karakteriyle tanımlanıyor.
Parlak donmuş kare durumunun ne kadar yaygın olduğunu size söyleyememe sebebim işte bu son cümle. Telemetri olaylarının şekilleri yazılmış ve tiplenmiş, doküman yorumlarında toplu bir yükleyicinin adı geçiyor ve o yükleyici mevcut değil. Üç kayıt tipi tanımlı ve kodun hiçbir yerinde hiç kurulmuyor. Bu yazıda hangi hata biçimlerinin önemli olduğuna dair verilen her yargı; bir saha kabul çalışmasından, bir hata enjeksiyon düzeneğinden ve bir test paketinden geliyor, bir cihaz filosundan değil. Bu, yukarıdaki her şeyin güvenilirliği üzerinde gerçek bir sınır.
Algılayıcıyı okumaktansa kullanmayı tercih ederseniz, My TV Player Windows uygulamasında öntanımlı olarak açık geliyor ve denemek için ücretsiz bir hesap oluşturabilirsiniz. İşini yaptığında onu görmeyeceksiniz: görüntü dört saniye takılıp geri gelecek ve ekranda, bir hükme varıldığını size söyleyen hiçbir şey belirmemiş olacak.
Bu makale neyi ölçtü?37 iddiaların her biri arkasında deliller barındırıyor
| Talep | Kanıt | sayıldı |
|---|---|---|
| Bu yazıdaki her şey, Windows uygulamasında libvlc 3.0.21 ve LibVLCSharp 3.9.7 üzerinde ölçüldü. İstatistik API'si libvlc 4.x'te farklı, dolayısıyla sayaç savları 3.x ile sınırlı.clients/windows/docs/PLAYBACK-WATCHDOG-PLAN.md:7 pins the head to LibVLCSharp 3.9.7 / libvlc 3.0.21; docs/research/MTP-PlaybackWatchdog-Spec-v1.0.md:5 scopes the spec to libvlc 3.x with forward-compatible notes for 4.x. | Şartname | Geçerli değil |
| libvlc hem bir donma hem de siyah bir yayın boyunca süresiz olarak Playing durumunda kalıyor ve EncounteredError sert açılış ile protokol hatalarına ayrılmış durumda; dolayısıyla canlı yayında baskın olan iki hata biçiminde neredeyse hiç tetiklenmiyor.docs/research/MTP-PlaybackWatchdog-Spec-v1.0.md:18 (within the cited 15-19 block), header checked, no confidentiality marking. | Şartname | Geçerli değil |
| F1'den F9'a dokuz arıza sınıfı tanımlı ve bunların yalnızca sekizi ilan edilebiliyor. F8 ClockStall hiçbir yerde ilan edilmiyor ve kendine ait bir taksonomi kodu yok. | n = 9 | 22 Ağu 2026 |
| Dokuz sınıfın üçü motor olaylarına biniyor. F3 motorun kendi hatası, F2 EndReached ya da sessiz bir Stopped üzerinden ilan ediliyor, F9 ise Buffering üzerinden. Geri kalanlar sayaçlardan çıkarsanıyor. | n = 3 | 22 Ağu 2026 |
| Arıza kartı eskiden her sınıf için tek bir cümle gösteriyordu; artık her sınıf, arızanın ilk kareden önce mi sonra mı düştüğüne de bağlı olan bir taksonomi koduna eşleniyor. | n = 1 | 22 Ağu 2026 |
| Bir istatistik örneği on bir alandan oluşuyor: bir monotonik zaman damgası ve on birikimli sayaç. Sekizinin farkı alınıyor; LostPictures ile LostAudioBuffers'ın farkını hiçbir kural almıyor. | n = 11 | 22 Ağu 2026 |
| Yedek sayaç yalnızca videoda var. DisplayedPictures kullanılamaz olduğunda DecodedVideo onun yerine geçiyor; DecodedAudio yalnızca bir ses akışının var olduğunu kanıtlamak için kullanılıyor ve hiçbir kural onu canlılık sinyali olarak okumuyor. | n = 1 | 22 Ağu 2026 |
| Hükümler önce erişim, sonra demuxer, en son sunum sırasıyla denetleniyor ve demuxer açlığından şüphelenilirken donma kuralları geri çekiliyor. | n = 1 | 22 Ağu 2026 |
| Şu anki ayar: F1 için 4 s sıfır bayt, F4 için 6 s, F5 ile F6 için 4 s, düşük kare hızlı bir tanıtım kartı için 15 s. | n = 1 | 22 Ağu 2026 |
| Null olabilen dört başlangıç etiketi, her koşulun başladığı anı tutuyor ve tek bir sağlıklı tick hepsini temizliyor. Beşinci bir sinyal, bozulma ve süreksizlik sıçraması, tersine çalışıyor: bir bitiş anı saklıyor ve kurulu olduğu sürece donma pencerelerini kısaltıyor. | n = 4 | 22 Ağu 2026 |
| Sayaç modeli ham MPEG-TS üzerinde kalibre edildi. Kaynak türü, telemetri ve ileride türe özel eşikler için "ts" ya da "hls" olarak taşınıyor ve bugün seçeneklerdeki hiçbir eşik HLS'e özgü değil. | n = 1 | 22 Ağu 2026 |
| libvlc 3.x bayt sayaçları 32 bit ve medya başına; bu yüzden yeniden açılışta sıfırdan başlıyor, uzun oturumlarda da başa sarıyor. Her iki durumda da delta negatife düşüyor.WatchdogStateMachine.cs:274-275 states both causes in place ("libvlc zeroes stats on reopen and int counters can wrap"). libvlc 3.x declares i_read_bytes and i_demux_read_bytes as int in libvlc_media_stats_t. | Şartname | Geçerli değil |
| Altı canlılık sayacından herhangi birindeki negatif bir delta, referansı sıfırlıyor ve o tick için değerlendirmeyi atlıyor. Bozuk birim ve süreksizlik deltaları bu korumanın parçası değil. | n = 6 | 22 Ağu 2026 |
| Kare sayacı 30 s'lik bir pencere boyunca, beş dal üzerinden Trusted, FallbackDecodedVideo ya da Untrusted olarak kalibre ediliyor; ayrıca donma hükümleri, bu oturumda bir video sayacının en az bir kez ilerlemiş olmasını şart koşuyor. | n = 5 | 22 Ağu 2026 |
| Gösterilen kare sayısını eksik bildiren yollar vout ve donanım kod çözme yolları, erişim modülleri değil.WatchdogStateMachine.cs:441 says "this access/vout combo underreports". The access module sits upstream of demux and decode and cannot influence i_displayed_pictures, which the vout core increments. | Şartname | Geçerli değil |
| Ses çalarken, saat ilerlerken ve bir video çıkışı ayaktayken ama bütün video sayaçları ölüyken geçen 45 birer saniyelik tick, hiçbir arıza ilan etmiyor ve Untrusted ile sonuçlanıyor. | n = 45 | 22 Ağu 2026 |
| Saniyede yaklaşık yarım kare ölçülen bir tanıtım kartı, donma penceresini kısaltmak yerine 15 s'ye genişletiyor ve tanıtım kartının tamamen durması genişletilmiş pencerede yine saptanıyor. | n = 1 | 22 Ağu 2026 |
| Sağlıklı videonun yanındaki bir ses takılması şüphe uyandırabiliyor ama hiçbir zaman hükme ulaşamıyordu; bu yüzden bir oturum ömrü boyunca daha hızlı örnekleme hızıyla SUSPECT durumunda kalıyor, kararlılık sinyalini hiç yaymıyor ve kurtarma defterini temizlenmemiş bırakıyordu; sonraki arızalar da tükenmiş bir merdiven devralıp doğrudan vazgeçme kartına gidiyordu. | n = 1 | 22 Ağu 2026 |
| Kullanıcının duraklatması donmuş sayaçları süresiz maskeliyor; bir sarma işlemi yalnızca 5 s maskeliyor; arabelleğe alma ise sunum hükümlerini askıya alırken sıfır bayt kuralı çalışmaya devam ediyor. | n = 3 | 22 Ağu 2026 |
| Şu anki siyah ayarı: bir piksel luma 24 ve altındayken siyah sayılıyor, bir örnek yüzde 98 siyah pikselde ve luma standart sapması 4.0 ve altındayken siyah oluyor, hüküm için de 96'ya 54 çözünürlükte yakalanmış 4 ardışık örnek gerekiyor. | n = 1 | 22 Ağu 2026 |
| Bir kurtarma denemesi doğrulandıktan sonraki 10 s boyunca, gereken ardışık siyah örnek sayısı 2'ye düşüyor. | n = 1 | 22 Ağu 2026 |
| 1500 ms bir aralık değil, yeniden kurma tabanı. Yoklama, zamanı geldiğinde ya da sonrasında gelen ilk mesajda tetikleniyor; dolayısıyla gerçekleşen aralık daha uzun: mesajların 1 Hz ile geldiği yerde 2 s, burada kaydedilen çalıştırmada olduğu gibi. | n = 1 | 22 Ağu 2026 |
| Bir gece sahnesi iki siyahlık eksenini birden geçemiyor ve letterbox bantlı içerik yarıdan az siyah çıkıyor; ikisi de testlerle siyah değil diye sabitlenmiş. | n = 2 | 22 Ağu 2026 |
| Yoklama, yayın akışı ilk kez görüntü verdikten hemen sonra tek seferlik bir tarama olarak çalışıyor, bir de şüpheyi bir video takılması uyandırdığında SUSPECT'e geçişte çalışıyor. Açılıştaki tarama kapandıktan sonra kararlı sağlık durumunda bir daha hiç çalışmıyor; dolayısıyla başka türlü sağlıklı bir akışta oynatma ortasında oluşan siyahlık hiçbir hüküm üretmiyor. | n = 2 | 22 Ağu 2026 |
| Yoklama, motorun kendi anlık görüntü çağrısı üzerinden yakalıyor ve donanımla kod çözülmüş bir yüzeyden anlık görüntü geri okumak, bir oynatıcının içerikle ilgisi olmayan sebeplerle boş kare döndürmesinin bilinen yerlerinden biri. Başarısız anlık görüntüyü destekleyici kanıt olarak ele alıyoruz; başarılı bir geri okumanın ne sıklıkta yanlışlıkla siyah geldiğine dair elimizde ölçüm yok.PlaybackEngine.Watchdog.cs:253 calls MediaPlayer.TakeSnapshot (libvlc_video_take_snapshot); the timeout path is handled at WatchdogStateMachine.cs:867-873. | Şartname | Geçerli değil |
| Her yoklama örneğinde 64 bitlik bir ortalama hash ile bir ortalama luma hesaplanıyor, ikisi de mesajın içinde yol alıyor ve kural motoru hiçbirini okumuyor. | n = 1 | 22 Ağu 2026 |
| Düz bir kare, bütün bitleri kurulu bir ortalama hash üretiyor; çünkü her hücre tam olarak global ortalamada duruyor ve karşılaştırma büyük ya da eşit. Dolayısıyla düz beyaz ile düz siyah bu hash için birbirinden ayırt edilemez. | n = 1 | 22 Ağu 2026 |
| libvlc içindeki bir use-after-free, CLR istisnası olarak değil native bir erişim ihlali olarak yüzeye çıkıyor; bu yüzden yönetilen bir catch hiç çalışmıyor.clients/windows/src/MyTvPlayer.Shared/Media/LiveTvPreviewMediaHelper.LibVlc.cs:165-180, reproduced 2026-08-19, 0xC0000005 in libvlc_media_player_release with a concurrent stats read. | Şartname | Geçerli değil |
| Önbelleğe alınmış bir LibVLCSharp Media sarmalayıcısı kendi native referansını tutuyor; bu yüzden havuz iş parçacığında bir medya değişiminden sonra, hiç istisna fırlatmadan ölü medyanın donmuş sayaçlarını döndürmeye devam ediyor.libvlc 3.x doxygen for libvlc_media_player_get_media: the returned media is reference counted and the caller must release it, which is why a cached LibVLCSharp Media keeps the old media alive and keeps answering. Recorded verbatim at clients/windows/src/MyTvPlayer.Shared/Media/Watchdog/PlaybackWatchdog.cs:322-325. | Şartname | Geçerli değil |
| Önbelleğe alınmış bir medya sarmalayıcısı, sağlıklı bir yayın akışında örnekleyicinin ölü medyanın donmuş sayaçlarını bildirmesine yol açtı ve bu ağ takılması olarak okundu. 2026-07-24 tarihinde saat 23:24'te bir saha kabul çalışması sırasında canlı olarak gözlendi. Sarmalayıcı her tick'te alınıp bırakılarak düzeltildi. | n = 1 | 24 Tem 2026 |
| Betiklenmiş on iki birer saniyelik tick, 2 s'de HEALTHY'ye, 7 s'de SUSPECT'e ulaşıyor ve 11 s'de 4000 ms anomali yaşıyla F5 VideoFreeze ilan ediyor. Ses sayacının da durduğu tıpatıp aynı betik, aynı tick'te F6 TotalFreeze ilan ediyor; SUSPECT anlık görüntülerinin zaman aşımına uğradığı üçüncü bir çalıştırma ise gerekçe dizesi "video stalled while audio keeps playing (snapshot timeout observed)" olacak şekilde uzatılmış hâlde F5 ilan ediyor. | n = 12 | 22 Ağu 2026 |
| Playback test paketi 108 testi 0 hatayla çalıştırıyor. | n = 108 | 22 Ağu 2026 |
| Beş basamak: yalnızca tam donmalar için bir duraklat ve oynat dürtmesi, tam bir yeniden uygulama, bir sonraki adaya geçiş, oynatıcının yeniden kurulması ve ardından vazgeçme. Tırmanmadan önce bir yeniden başlatma denemesi var ve kanal açıldıktan sonraki 15 s içinde çıkan bir arıza, yeniden uygulamadan değil geçişten giriyor. | n = 5 | 22 Ağu 2026 |
| Oynatıcıyı yeniden kurma basamağının host tarafında henüz bir karşılığı yok ve sürüme çıkan ayarlarda kapalı; bu yüzden durdurma zaman aşımı yaşanan bir olay şu anda ön plandaki merdiveni bitiriyor ve vazgeçmeye gidiyor. | n = 1 | 22 Ağu 2026 |
| k'ıncı deneme, simetrik jitter ile min(taban çarpı (2^k - 1), tavan) kadar bekliyor; yani ilk deneme hemen tetikleniyor ve ikinciden itibaren büyüme tavana kadar 1 s, 3 s, 7 s şeklinde gidiyor. Jitter tavandan sonra uygulanıyor, dolayısıyla tavana dayanan beklemeler 30 s çevresinde simetrik dağılıyor, gerçek üst sınır ise 36 s. | n = 1 | 22 Ağu 2026 |
| Yoklama, uygulamanın kendi yerel veri klasörüne dönüşümlü iki PNG dosyası yazıyor ve üzerlerine yazıyor. Arıza olayları yerel loga gidiyor; orada bir kanal, bir SHA-256'nın ilk 16 onaltılık karakteriyle tanımlanıyor, hiçbir zaman URL ile değil. | n = 1 | 22 Ağu 2026 |
| Telemetri kayıt tipleri tanımlanmış ama hiç kurulmuyor ve doküman yorumlarında adı geçen toplu yükleyici mevcut değil. | n = 3 | 22 Ağu 2026 |