node-mac-recorder
Version:
Native macOS screen recording package for Node.js applications
221 lines (169 loc) • 7.79 kB
Markdown
🔴 KRITIK BUG BULUNDU: Area Recording Tam Ekranı Kaydediyor!
Evet, ciddi bir bug var - belirtilen alan yerine tam ekranı kaydediyor olabilir. İşte analiz:
---
🐛 Bug #1: ScreenCaptureKit - sourceRect Bounds Validation Hatası
Dosya: src/screen_capture_kit.mm:1542-1554
Problem:
if (displayRelativeX >= 0 && displayRelativeY >= 0 &&
displayRelativeX + cropWidth <= displayBounds.size.width &&
displayRelativeY + cropHeight <= displayBounds.size.height) {
CGRect sourceRect = CGRectMake(displayRelativeX, displayRelativeY, cropWidth, cropHeight);
streamConfig.sourceRect = sourceRect;
MRLog(@"✂️ Crop sourceRect applied: ...");
} else {
NSLog(@"❌ Crop coordinates out of display bounds - skipping crop");
// ⚠️ BURADA sourceRect SET EDİLMİYOR!
}
Sorun:
- Koordinatlar display bounds dışındaysa sourceRect hiç set edilmiyor
- Bu durumda ScreenCaptureKit tam ekranı kaydediyor (default behavior)
- Hata log'u atıyor ama kayıt devam ediyor, kullanıcı fark etmiyor!
Bu ne zaman oluşur:
1. ✅ Koordinatlar 0.1 piksel bile bounds'un dışındaysa
2. ✅ Floating point precision hataları (örn: 1920.0000000001)
3. ✅ Retina display scaling hesaplamaları yanlışsa
4. ✅ Çoklu ekran sistemlerinde display-relative dönüşüm yanlışsa
---
🐛 Bug #2: Bounds Check'te Floating Point Precision Problemi
Kod:
if (displayRelativeX >= 0 && displayRelativeY >= 0 &&
displayRelativeX + cropWidth <= displayBounds.size.width &&
displayRelativeY + cropHeight <= displayBounds.size.height)
Sorun:
- <= strict comparison kullanıyor
- Floating point hesaplamalardan sonra 1920.0000000001 gibi değerler 1920.0 ile eşleşmiyor
- JavaScript'ten gelen koordinatlar Math.round() ile yuvarlanmış olabilir ama native tarafta double precision
kullanılıyor
Örnek:
// JavaScript (index.js)
captureArea: { x: 0, y: 0, width: 1920, height: 1080 } // Integer
// Native (Objective-C)
CGFloat cropWidth = [captureRect[@"width"] doubleValue]; // 1920.0
CGFloat displayWidth = displayBounds.size.width; // 1920.0000000001 (floating point)
// Comparison FAILS!
if (0 + 1920.0 <= 1920.0000000001) // TRUE
Ancak dikkat:
- Display dimensions genelde exact integers (1920x1080)
- ANCAK Retina ekranlarda logical size / 2 = 960.0 gibi değerler oluşabilir
- Çoklu ekranlarda farklı scaling faktörleri koordinat dönüşümlerini bozabilir
---
🐛 Bug #3: Koordinat Dönüşüm Zinciri Problemi
Akış:
1. JavaScript (index.js:363-575) - Global koordinatları display-relative'e çeviriyor
2. Native StartRecording (mac_recorder.mm:296) - CGRect olarak alıyor
3. ScreenCaptureKit (screen_capture_kit.mm:1530) - Display-relative olarak kabul ediyor
Potansiyel Problem Noktaları:
3a) JavaScript Normalizasyon Hatası
index.js:476-486:
const tolerance = 1; // 1 pixel tolerance
const isRelativeToDisplay = () => {
const endX = parsedArea.x + parsedArea.width;
const endY = parsedArea.y + parsedArea.height;
return (
parsedArea.x >= -tolerance &&
parsedArea.y >= -tolerance &&
endX <= targetRect.width + tolerance &&
endY <= targetRect.height + tolerance
);
};
Sorun:
- 1 pixel tolerance çok küçük olabilir!
- Retina ekranlarda logical vs physical pixel karışıklığı
- -tolerance negatif koordinatlara izin veriyor ama native kod >= 0 bekliyor
3b) Clamping/Clipping Yetersizliği
index.js:509-527:
if (relativeX < 0) {
relativeWidth += relativeX; // Width'i azaltıyor
relativeX = 0;
}
// Height için benzer
Sorun:
- Clipping yapıyor ama sonuç <= bounds check'ini geçemeyebilir
- Örnek: x=0, width=1920.5 → bounds check fail → tam ekran kaydedilir
---
🐛 Bug #4: AVFoundation'da Daha Büyük Sorun - Varsayılan Full Screen
Dosya: src/avfoundation_recorder.mm:233-236
} else {
g_avCaptureRect = CGRectZero; // Full screen
NSLog(@"🖥️ Full screen capture using physical dimensions ...");
}
Sorun:
- CGRectIsEmpty(captureRect) check'i var
- ANCAK JavaScript'ten CGRectNull gelirse (boş alan), bu check FALSE döner
- CGRectNull vs CGRectZero vs CGRectEmpty karışıklığı
JavaScript tarafından gönderilen değer:
// mac_recorder.mm:269
CGRect captureRect = CGRectNull; // Default değer
// Eğer options.captureArea yoksa:
if (!CGRectIsNull(captureRect)) {
sckConfig[@"captureRect"] = @{ ... }; // Bu ÇALIŞMAZ, captureRect NULL!
}
Sonuç:
- captureRect set edilmezse ScreenCaptureKit/AVFoundation full screen kaydeder
---
🎯 Root Cause: Multi-Step Validation Eksikliği
Koordinat doğrulama 3 farklı yerde yapılıyor ama tutarsız:
1. ✅ JavaScript (index.js) - Normalize ediyor, clamp ediyor
2. ❌ ScreenCaptureKit - Strict bounds check, fail ederse sourceRect yok → full screen
3. ⚠️ AVFoundation - Scaling yapıyor ama validation eksik
---
🔬 Tespit Yöntemi
Bu bug'ı şu şekilde tespit edebilirsin:
const recorder = new MacRecorder();
// Test case: Display bounds'un HEMEN dışında bir alan
const displays = await recorder.getDisplays();
const mainDisplay = displays.find(d => d.isPrimary);
await recorder.startRecording('test.mov', {
displayId: mainDisplay.id,
captureArea: {
x: 0,
y: 0,
width: mainDisplay.width + 0.5, // 0.5 piksel daha fazla!
height: mainDisplay.height
}
});
Beklenen: Kırpılmış/hatalı alan kaydedilmeli
Gerçekte Oluyor: Tam ekran kaydediliyor (sourceRect skip ediliyor)
---
✅ Çözüm Önerileri
Fix #1: Tolerance Ekle (ScreenCaptureKit)
// Line 1542'de
const CGFloat CROP_TOLERANCE = 2.0; // 2 pixel tolerance
if (displayRelativeX >= -CROP_TOLERANCE &&
displayRelativeY >= -CROP_TOLERANCE &&
displayRelativeX + cropWidth <= displayBounds.size.width + CROP_TOLERANCE &&
displayRelativeY + cropHeight <= displayBounds.size.height + CROP_TOLERANCE) {
// Clamp coordinates to valid range
displayRelativeX = MAX(0, displayRelativeX);
displayRelativeY = MAX(0, displayRelativeY);
cropWidth = MIN(cropWidth, displayBounds.size.width - displayRelativeX);
cropHeight = MIN(cropHeight, displayBounds.size.height - displayRelativeY);
CGRect sourceRect = CGRectMake(displayRelativeX, displayRelativeY, cropWidth, cropHeight);
streamConfig.sourceRect = sourceRect;
} else {
NSLog(@"❌ CRITICAL: Crop coordinates SIGNIFICANTLY out of bounds - ABORTING recording");
// Kayıt başlatmayı reddet, hata döndür
SCKFailScheduling();
return;
}
Fix #2: JavaScript Tolerance Artır
// index.js:476
const tolerance = 3; // Retina ekranlar için daha büyük tolerance
Fix #3: Varsayılan Davranışı Değiştir
// Bounds check fail ederse TAM EKRAN yerine HATA DÖNDÜR
NSLog(@"❌ Invalid crop coordinates - refusing to record");
*error = [NSError errorWithDomain:@"ScreenCapture"
code:-1
userInfo:@{NSLocalizedDescriptionKey: @"Crop area out of display bounds"}];
return NO; // Recording başarısız
---
📝 Sonuç
EVET, ciddi bir bug var:
- ✅ Area recording bounds check'i fail ederse tam ekran kaydediyor
- ✅ Floating point precision hataları check'i fail ettiriyor
- ✅ Kullanıcı bilgilendirilmiyor (sadece log)
- ✅ JavaScript'ten gelen koordinatlar native validation'dan geçemeyebiliyor
En riskli senaryolar:
1. Retina display + area recording
2. Çoklu ekran + area recording
3. 0.5 piksel hassasiyetle alan seçimi (UI'dan gelebilir)