انتقل إلى المحتوى الرئيسي
🤖AI-generated documentation curatedAI Generated
This page was drafted by an AI assistant and may contain inaccuracies. This content has been reviewed by a human curator.
About content generation types
🤖
AI GeneratedPage drafted entirely by AI from codebase or prompt instructions.
(e.g., docs generated from codebase analysis)
← this page
✋→🤖
AI TransformattedHuman provided raw material; AI restructured it into a different format.
(e.g., livestream → blog post, meeting notes → docs)
Human GeneratedPage written entirely by a human author.
(e.g., hand-written tutorial)
More info about content generation types ↗

طريقة تزامن الإطارات

هالصفحة غوص تقني عميق ببروتوكول الالتقاط المبوّب بعدد الإطارات تبع SkellyCam. لنظرة عامة أعلى مستوى، شوف تزامن مثالي للإطارات.

نظام تزامن الإطارات بالكامل هو حلقة ساخنة — كل عملية بدورة الالتقاط حرجة زمنياً، وأي تأخير بيأثر مباشرة على الدقة الزمنية.

حلقة الالتقاط

كل كاميرا بتشغّل الحلقة التالية بعمليتها الخاصة من multiprocessing.Process. الحلقة بتشتغل باستمرار من لحظة توصيل الكاميرات لحد ما تنطفي.

شو يعني "ذري" هون

بهالسياق، "ذري" يعني إنو أعلام التسجيل بتنقرأ كعملية واحدة غير قابلة للتجزئة نسبة لدورة الإطار. قيم الأعلام ما فيها تتغير بين قراءتها والتصرف بناءً عليها، لأنو علم grabbing_frame ما بينمسح إلا بعد ما تنقرأ الأعلام. هاد بيمنع حالات السباق عند حدود بدء/إيقاف التسجيل وأخطاء الواحد بعدد إطارات الفيديوهات الناتجة.

خطوة بخطوة

الخطوةالعمليةموقع الكودملاحظات
1فحوصات التحديثcamera_loop_update_checks.pyبتفحص أوامر الإيقاف المؤقت، تحديثات الإعدادات، ومعلومات التسجيل الجديدة
2بوابة الإيقاف المؤقتopencv_camera_loop.py:66-68إذا is_paused، انتظار دوّار 1 ملي ثانية وتخطي للتكرار التالي
3بوابة التزامنcamera_orchestrator.py:93-106should_grab_by_id() بترجع True بس لما عدد إطارات هالكاميرا يكون ≤ كل الكاميرات التانية
4الالتقاطopencv_get_frame.pycv2.VideoCapture.grab() — بتثبّت صورة المستشعر بذاكرة التخزين المؤقت للسائق
5الاسترجاعopencv_get_frame.pycv2.VideoCapture.retrieve() — بتفك تشفير الإطار لمصفوفة numpy مخصصة مسبقاً
6أعلام التسجيلopencv_camera_loop.py:90-94قراءة الأعلام قبل مسح grabbing_frame لمنع حالات السباق
7الذاكرة المشتركةcamera_shared_memory_ring_buffer.pyput_frame(overwrite=True) — مستهلكو البث ما فيهم يحظروا الالتقاط
8التسجيلhandle_video_recording_loop.pyالكتابة لـ cv2.VideoWriter إذا should_record_frame مفعّل
9الإنهاءhandle_video_recording_loop.pyإغلاق الكاتب والتفريغ إذا should_finish_recording مفعّل
10الزيادةopencv_camera_loop.pyتحديث frame_count، فتح البوابة للكاميرات التانية

بوابة التزامن

الـ CameraOrchestrator بيفرض تزامن على مستوى الإطار عبر should_grab_by_id():

def should_grab_by_id(self, camera_id) -> bool:
if not self.all_ready:
return False
return self._all_camera_counts_greater_than_or_equal_to_camera(camera_id)

الكاميرا فيها تلتقط إطارها التالي بس لما يكون عدد إطاراتها هو الأقل (أو متعادل بالأقل) بين كل الكاميرات. هاد بيضمن ما في كاميرا بتسبق كاميرا تانية بأكتر من إطار واحد.

كل الكاميرات بتستطلع هالبوابة بنفس الوقت — لما يوصلوا كلهم لنفس عدد الإطارات، كلهم بيعدّوا البوابة تقريباً بنفس الوقت وبيلتقطوا إطاراتهم التالية بتقارب زمني.

حدود التسجيل

بدء وإيقاف التسجيل بيتنسقوا عبر كل الكاميرات من خلال أعداد صحيحة مشتركة من multiprocessing.Value بيديرها المنسّق:

  • first_recording_frame_number — بيتحط لما يبدأ تسجيل. قيمته الأولية -1.
  • last_recording_frame_number — بيتحط لما يوقف تسجيل. قيمته الأولية -1.

كل كاميرا بتفحص هالقيم بكل إطار:

def should_record_frame_number(self, frame_number: int) -> tuple[bool, bool]:
should_record_frame = False
should_finish_recording = False

if self.first_recording_frame_number.value != -1:
if frame_number >= self.first_recording_frame_number.value:
should_record_frame = True

if self.last_recording_frame_number.value != -1:
if frame_number < self.last_recording_frame_number.value:
should_record_frame = True
elif frame_number >= self.last_recording_frame_number.value:
should_record_frame = False
should_finish_recording = True

return should_record_frame, should_finish_recording

منع خطأ الواحد

أعلام التسجيل بتنقرأ مباشرة بعد التقاط الإطار وقبل مسح علم grabbing_frame:

# CRITICAL: Get recording flags BEFORE unsetting 'grabbing_frame'
# to avoid potential race-condition-generating flag setting gaps
(should_record_frame,
should_finish_recording) = orchestrator.should_record_frame_number(
frame_number=frame_rec_array.frame_metadata.frame_number[0])

self_status.grabbing_frame.value = False # Only AFTER reading flags

هالترتيب بيمنع حالة سباق وين حد التسجيل ممكن يتغير بين قراءة العلم واستخدامه. لأنو كل الكاميرات ماشية بخطوة واحدة، كلها بتعبر حد التسجيل بنفس رقم الإطار، فبتنتج فيديوهات بعدد إطارات متطابق.

الإيقاف المؤقت وتحديثات الإعدادات

الإيقاف المؤقت

الإيقاف المؤقت بيتحكم فيه عبر طرق pause() / unpause() تبع المنسّق، اللي بتحط should_pause على كل الكاميرات وبتستنى لحد ما is_paused ينتشر:

if self_status.is_paused.value:
wait_1ms()
continue # Skip frame grab entirely

لما تكون متوقفة مؤقتاً، الكاميرات بتستنى بانتظار دوّار بفترات 1 ملي ثانية. هاد سريع كفاية للاستخدام التفاعلي مع استهلاك حد أدنى للمعالج.

تحديثات الإعدادات

تحديثات الإعدادات (الدقة، التعريض، تغيير الترميز) بتنفحص ببداية كل تكرار للحلقة:

if not update_camera_settings_subscription.empty():
self_status.updating.value = True
extracted_config = apply_camera_configuration(cv2_video_capture, new_config)
self_status.updating.value = False

بينما كاميرا عم تحدّث (updating.value = True)، خاصية all_ready تبع المنسّق بترجع False، اللي بتمنع كل الكاميرات من الالتقاط. هاد بيضمن إنو تغييرات الإعدادات ما بتصير بنص دورة الإطار.

أعلام حالة الكاميرا

كل كاميرا بتحافظ على مجموعة أعلام بوليانية من multiprocessing.Value للتنسيق بين العمليات:

العلمالغرض
connectedعتاد الكاميرا تهيّأ وجاهز
grabbing_frameحالياً جوا دورة الالتقاط/الاسترجاع
recording_in_progressمسجّل الفيديو نشط
is_recording_frameحالياً عم يكتب إطار على القرص
is_pausedمتوقف مؤقتاً (ما عم يلتقط)
should_pauseتم استلام أمر الإيقاف المؤقت
updatingتحديث الإعدادات قيد التنفيذ
frame_countآخر رقم إطار تم التقاطه (multiprocessing.Value('q'))
error / closing / closedحالات الخطأ والإغلاق

خاصية ready بتبوّب المنسّق:

@property
def ready(self) -> bool:
return all([self.connected,
not self.should_pause,
not self.should_close,
not self.closing,
not self.closed,
not self.updating,
not self.error])

حاجز التهيئة

قبل ما تبدأ الحلقة الرئيسية، كل الكاميرات لازم تكمّل التهيئة:

  1. الاتصال بالعتاد ونشر الإعدادات المستخرجة
  2. الانتظار لإعداد الذاكرة المشتركة من العملية الرئيسية
  3. تعيين connected = True
  4. الانتظار لحد كل الكاميرات تحط connected = True (حاجز عبر orchestrator.all_ready)
  5. تفريغ ذاكرة العتاد المؤقتة بقراءة إطارات وهمية
  6. تهيئة مصفوفة الإطار بـ frame_number = -1

بس بعدين بتبدأ حلقة الالتقاط المتزامنة — مما بيضمن إنو كل الكاميرات بتبدأ من الإطار 0 مع بعض.

التعامل مع الأخطاء

إذا أي كاميرا واجهت خطأ:

  1. signal_error() بتحط error = True و connected = False
  2. ipc.kill_everything() بتحط علم القتل العام
  3. كتلة finally بتضمن إنو أي تسجيل قيد التنفيذ بينتهي (ملف الفيديو بينسكر، الطوابع الزمنية بتتفرّغ)
  4. الذاكرة المشتركة للكاميرا بتنسكر والتقاط OpenCV بينحرر

هاد بيضمن إنو حتى بحالات الانهيار، البيانات المسجلة بتنحفظ بأكمل شكل ممكن.