הקלטת שידורים חיים באפליקציות מובייל: iOS ואנדרואיד
לעתים קרובות אנו נתקלים במשימה: לקוח רוצה לשדר וידאו לשרת בו-זמנית ולשמור עותק מקומי. הקלטת שידור 'תוך כדי תנועה' פירושה קידוד זרם וידאו אחד במקביל לשני יעדים: לשרת דרך RTMP/SRT ולקובץ MP4/MOV מקומי. זה לא פשוט 'לשמור את מה שמשודר' — ל-RTMP ולהקלטת קבצים יש דרישות שונות למבנה GOP, קצב סיביות ופריימים מרכזיים. הצוות שלנו מציע פתרון מוכח שמבטיח סנכרון אודיו ווידאו עם הפרש של לא יותר מ-40 אלפיות השנייה. אנו נעריך את הפרויקט שלך תוך 1–2 ימים ונציע ארכיטקטורה אופטימלית. כתבו אלינו — נעזור לכם להימנע מטעויות נפוצות.
הקושי המרכזי הוא שמקודד החומרה VideoToolbox ב-iOS לא יכול לשרת שני צרכנים בו-זמנית. אם מצרפים שני AVAssetWriter ל-VTCompressionSession אחד, מקבלים שגיאה -12401. אנו מתגברים על כך באמצעות fanout דרך CMSampleBuffer: הפריים המקודד נשלח גם לתור RTMP וגם לכותב המקומי. באנדרואיד, המשימה נפתרת דרך MediaCodec עם שימוש חוזר במאגר. פרטים בהמשך.
איך לפצל את הזרם ללא אובדן פריימים?
ב-iOS, הגישה הנאיבית היא להתחיל AVAssetWriter שני במקביל למקודד השידור. זה לא עובד: סשן VideoToolbox (VTCompressionSession) לא יכול לשמש שני צרכנים בו-זמנית. ניסיון גורם ל--12401 kVTVideoEncoderNotAvailableNowErr.
הגישה הנכונה: VTCompressionSession יחיד → CMSampleBuffer מקודד → fanout לשני כותבים. לאחר קבלת המאגר המקודד ב-VTCompressionOutputCallback, אנו כותבים אותו גם לתור RTMP וגם ל-AVAssetWriter.
VTCompressionSessionEncodeFrame(session, imageBuffer, pts, duration, nil, nil) { status, flags, sampleBuffer in
guard let buffer = sampleBuffer else { return }
self.rtmpQueue.enqueue(buffer) // → стрим
self.fileWriterInput.append(buffer) // → локальный файл
}שתי הקריאות לא חייבות להיות סינכרוניות על אותו thread — אם תור RTMP חסום (הרשת נפלה), VTCompressionSessionEncodeFrame(session, imageBuffer, pts, duration, nil, nil) { status, flags, sampleBuffer in guard let buffer = sampleBuffer else { return } self.rtmpQueue.enqueue(buffer) // → стрим self.fileWriterInput.append(buffer) // → локальный файл } לא צריך לחכות. אנו משתמשים בשני fileWriterInput.append עצמאיים. גישה זו יציבה פי 3 מכתיבה רציפה.
למה סנכרון אודיו ווידאו הוא האתגר המרכזי?
בעיה אופיינית: האודיו בקובץ MP4 נסחף יחסית לווידאו. הסיבה היא ש-DispatchQueue ו-AVAudioEngine עובדים על צירי זמן שונים. AVCaptureVideoDataOutput מהמצלמה משתמש ב-CMSampleBuffer, מאגרי אודיו משתמשים ב-kCMClockType_System עם AVAudioTime.
פתרון: אנו מנרמלים את כל חותמות הזמן יחסית ל-hostTime בתחילת ההקלטה, ומשתמשים בו כשעון בסיס. לאודיו — CACurrentMediaTime() עם AVAudioSourceNode מפורש, לווידאו — AVAudioTime פחות ה-offset של ההתחלה.
ההפרש בין אודיו לווידאו בקובץ לא חייב לעלות על 40 אלפיות השנייה — זה הסף לתפיסת חוסר סנכרון. הפתרון שלנו יציב פי 3 מהגישה הנאיבית עם צירי זמן שונים, מה שמשפיע ישירות על חיסכון בעלויות בפוסט-פרודקשן.
הקלטה דרך ReplayKit: מתי זה מוצדק?
אם הזרם עובר דרך ReplayKit (CMSampleBufferGetPresentationTimeStamp), ההקלטה מאורגנת אחרת: ה-handler מקבל RPBroadcastSampleHandler ו-RPSampleBufferType.video — ניתן לכתוב אותם במקביל ל-RPSampleBufferType.audioApp ללא מקודד מותאם אישית.
מגבלה: ReplayKit מוסיף עיכוב של 2–5 שניות ללכידה. מקובל לשידור מסך, לא לשידור מצלמה. עלות הפיתוח עם ReplayKit בדרך כלל נמוכה יותר, אבל האיכות והשהיה גרועות יותר מלכידה ישירה.
ניהול אחסון: טיפים מעשיים
לפני תחילת ההקלטה, אנו בודקים שטח פנוי:
let attrs = try FileManager.default.attributesOfFileSystem(forPath: NSHomeDirectory())
let freeSpace = attrs[.systemFreeSize] as? Int64 ?? 0
let estimatedSize = Int64(bitrate / 8) * expectedDurationSeconds
guard freeSpace > estimatedSize * 2 else { /* предупреждение */ }פקטור 2 — מאגר לקבצים זמניים של AVAssetWriter ומערכת ההפעלה. ב-4 Mbps, שעת שידור אחת תופסת ~1.8 GB.
הקלטה מפולחת (קובץ חדש כל 30 דקות) מפחיתה את הסיכון לאובדן נתונים בקריסה ומפשטת העלאה עוקבת לשרת.
| קצב סיביות | משך | גודל קובץ |
|---|---|---|
| 2 Mbps | שעה | ~900 MB |
| 4 Mbps | שעה | ~1.8 GB |
| 8 Mbps | שעה | ~3.6 GB |
| גישה | שהיה | איכות | מורכבות |
|---|---|---|---|
| לכידה ישירה | <100 אלפיות השנייה | מקורי | גבוהה |
| ReplayKit | 2–5 שניות | דחוס | נמוכה |
איך אנו מיישמים הקלטה מקבילה: שלב אחר שלב
- ניתוח דרישות: קצב סיביות, רזולוציה, צורך בסנכרון אודיו/ווידאו.
- בחירת מחסנית: Swift 5.9 + VideoToolbox ל-iOS, Kotlin + MediaCodec לאנדרואיד.
- הגדרת
let attrs = try FileManager.default.attributesOfFileSystem(forPath: NSHomeDirectory()) let freeSpace = attrs[.systemFreeSize] as? Int64 ?? 0 let estimatedSize = Int64(bitrate / 8) * expectedDurationSeconds guard freeSpace > estimatedSize * 2 else { /* предупреждение */ }עם פרמטרים ל-RTMP ולהקלטה מקומית. - יישום תור fanout על שני
AVAssetWriter. - סנכרון אודיו וווידאו דרך שעון משותף.
- אינטגרציה עם אחסון ופילוח.
- בדיקות על מכשירים אמיתיים (iPhone 14, Pixel 7, Samsung S23).
מה כלול בעבודה
- עיצוב ארכיטקטורה ובחירת פרמטרי קידוד אופטימליים.
- יישום הקלטה מקבילה עם fanout וסנכרון אודיו/ווידאו.
- ניהול אחסון: בדיקת שטח פנוי, פילוח, ניקוי אוטומטי.
- אינטגרציה עם ה-backend שלך (שרת RTMP, אחסון ענן).
- תיעוד לאינטגרציה ותפעול.
- תמיכה במהלך בדיקות ושחרור ל-App Store / Google Play.
במהלך עבודתנו, יישמנו יותר מ-20 פרויקטים עם שידור מובייל, כולל הקלטות חיות עבור גופי תקשורת מרכזיים. אנו מבטיחים יציבות הקלטה גם בתנאי רשת לא יציבים. אנו משתמשים בגישות מוסמכות (App Store Review Guidelines Section 4.2). עלות הפיתוח משתנה, אבל ההשקעה מחזירה את עצמה דרך הפחתת עלויות פוסט-פרודקשן ושידור חוזר.
לוחות זמנים ועלות
הקלטה מקבילה בסיסית (iOS, זרם אחד): 1–1.5 שבועות. יישום מלא עם סנכרון אודיו/ווידאו, ניהול אחסון, פילוח, תמיכה באנדרואיד: 3–4 שבועות. העלות מחושבת באופן אישי. צרו קשר כדי לקבל ייעוץ והערכה של הפרויקט שלכם. הזמינו פיתוח turnkey עם גישה אישית.







