יישום פילטרי וידאו בזמן אמת באפליקציות מובייל
פיתוח פילטרי וידאו לאפליקציות מובייל הוא תמיד פשרה בין איכות תמונה לביצועים. לקוח אחד הגיע עם משימה: להחיל פילטרי LUT על וידאו 4K ב-60fps בזמן אמת. באייפון 14 Pro, בדיקות ראשוניות הניבו 12 fps—ונפלו פריימים לסירוגין. הבעיה הייתה יצירת CIContext חדש עבור כל פריים. לאחר אופטימיזציה, ניצול ה-GPU ירד ל-30%, וה-fps התייצב על 60. אנו משתמשים ב-shaders של GPU לעיבוד פריימים. ב-iOS: Swift עם Metal; באנדרואיד: GLSL או Vulkan. לפרויקטים חוצי-פלטפורמות, אנו מיישמים מודולים מקוריים דרך ערוצי פלטפורמה.
שני מצבים: תצוגה מקדימה וייצוא
תצוגה מקדימה בזמן אמת (במהלך השמעה) דורשת עיבוד GPU ללא כתיבת התוצאה לקובץ. ייצוא—הקלטה סופית עם הפילטר המוחל—יכול לקחת שניות אך חייב להיות מדויק ברמת הפריים.
למה CIContext יחיד חשוב
יצירת CIContext חדש לכל פריים היא רוצחת ביצועים. באייפון 13, זה מניב 3–4 fps במקום 30. ה-context נוצר פעם אחת וממוחזר. CIContext עם גיבוי Metal הוא חובה: CIContext(mtlDevice: MTLCreateSystemDefaultDevice()!). ללא ציון מפורש, חלק מהמכשירים נופלים לעיבוד CPU, מה שגורם לגמגום וידאו.
איך להימנע מירידות FPS במהלך ייצוא
ה-CVPixelBuffer מ-renderContext.newPixelBuffer() חייב להיות מוחזר מיד לאחר finish(withComposedVideoFrame:). החזקת הפניה מובילה לדלדול מאגר הבופרים ולקריסות עם kCVReturnInvalidArgument. באנדרואיד: GPUImageFilter אינו thread-safe—לא ניתן להחיל את אותו מופע ממספר תהליכונים בו-זמנית.
השוואת פלטפורמות
| פלטפורמה | תצוגה מקדימה | ייצוא | כלים |
|---|---|---|---|
| iOS | AVPlayer + Metal | AVVideoComposition + CIContext | Swift, Metal, Core Image |
| אנדרואיד | GPUImageView + GLShader | media3 Transformer + GlEffect | Kotlin, GPUImage, media3 |
| Flutter | PlatformView (קוד מקורי) | MethodChannel + פילטר מקורי | Dart + Swift/Kotlin |
ביצועים: CPU לעומת GPU
| גישה | תפוקה (60fps 1080p) | השהיה |
|---|---|---|
| CPU (vImage) | 12–15 fps | ~70 ms |
| GPU (Metal/GL) | 60 fps | ~16 ms |
iOS: AVVideoComposition + Core Image
AVVideoCompositionCoreAnimationTool מתאים לשכבות סטטיות כמו טקסט ולוגואים. עבור פילטרים ברמת פיקסל, אנו משתמשים ב-AVVideoComposition עם AVVideoCompositing מותאם אישית:
class FilterCompositor: NSObject, AVVideoCompositing {
func startRequest(_ asyncVideoCompositionRequest: AVAsynchronousVideoCompositionRequest) {
guard let frame = asyncVideoCompositionRequest.sourceFrame(byTrackID: trackID) else { return }
let ciImage = CIImage(cvPixelBuffer: frame)
let filtered = applyFilter(ciImage)
let output = asyncVideoCompositionRequest.renderContext.newPixelBuffer()!
ciContext.render(filtered, to: output)
asyncVideoCompositionRequest.finish(withComposedVideoFrame: output)
}
}עבור פילטרי LUT, אנו משתמשים ב-class FilterCompositor: NSObject, AVVideoCompositing { func startRequest(_ asyncVideoCompositionRequest: AVAsynchronousVideoCompositionRequest) { guard let frame = asyncVideoCompositionRequest.sourceFrame(byTrackID: trackID) else { return } let ciImage = CIImage(cvPixelBuffer: frame) let filtered = applyFilter(ciImage) let output = asyncVideoCompositionRequest.renderContext.newPixelBuffer()! ciContext.render(filtered, to: output) asyncVideoCompositionRequest.finish(withComposedVideoFrame: output) } } עם טבלת 64×64×64. למידע נוסף על Core Image.
אנדרואיד: GPUImage ואפקטי media3
תצוגה מקדימה בזמן אמת משתמשת ב-CIColorCubeWithColorSpace עם GPUImageView מותאם אישית. ה-Shaders נכתבים ב-GLSL:
precision mediump float;
uniform sampler2D inputImageTexture;
varying vec2 textureCoordinate;
void main() {
vec4 color = texture2D(inputImageTexture, textureCoordinate);
float luma = dot(color.rgb, vec3(0.299, 0.587, 0.114));
gl_FragColor = vec4(mix(vec3(luma), color.rgb, 1.3), color.a);
}לייצוא עם פילטר, אנו משתמשים ב-GPUImageFilter עם precision mediump float; uniform sampler2D inputImageTexture; varying vec2 textureCoordinate; void main() { vec4 color = texture2D(inputImageTexture, textureCoordinate); float luma = dot(color.rgb, vec3(0.299, 0.587, 0.114)); gl_FragColor = vec4(mix(vec3(luma), color.rgb, 1.3), color.a); } . media3 Transformer מקבל את ה-shader ומחיל אותו על כל פריים במהלך טרנסקודינג.
Flutter. פילטרי וידאו מלאים בזמן אמת דורשים קוד פלטפורמה. תוספים מקוריים עם MethodChannel הם הגישה המעשית היחידה לאפליקציות ייצור.
החלת פילטרים על הקלטת מצלמה חיה
תרחיש תובעני עוד יותר הוא החלת פילטרים במהלך לכידה. ב-iOS: GlEffect + MatrixTextureProcessor → AVCaptureSession → Metal shader → עיבוד לתוך AVCaptureVideoDataOutput. השהיית השרשרת חייבת להיות < 16 ms עבור 60fps. פורמט הפיקסלים SampleBufferDelegate (YUV) מומר למרקם Metal מהר יותר מאשר BGRA.
באנדרואיד: MTKView + kCVPixelBufferPixelFormatTypeKey: kCVPixelFormatType_420YpCbCr8BiPlanarFullRange → GLES shader → CameraX Analysis. שימוש ב-ImageAnalysis.Analyzer משליך פריימים ישנים אם ה-GPU לא סיים לעבד את הפריים הקודם.
תהליך העבודה
- ניתוח: הערכת דרישות הפילטר, מכשירי יעד, ו-fps רצוי.
- עיצוב: בחירת ארכיטקטורה (מקורית/חוצת-פלטפורמות), אב-טיפוס של shaders.
- יישום: כתיבת shaders של GPU, אינטגרציה עם AVFoundation/Media3, אופטימיזציית ביצועים.
- בדיקות: על מכשירים אמיתיים (iPhone 8–14, Samsung Galaxy S10–S23), מדידת fps, בדיקת חפצים (artifacts).
- פריסה: פרסום ל-App Store/Google Play, הגדרת TestFlight/App Distribution.
טעות נפוצה בייצוא
לעתים קרובות, מפתחים שוכחים לעשות שימוש חוזר ב-`CIContext`—מה שגורם לירידת fps ל-2–3. כמו כן, חיוני להחזיר את `CVPixelBuffer` מיד לאחר השימוש; אחרת, מאגר הבופרים מתרוקן.מה כלול בעבודה
- קוד מקור ל-shaders ואינטגרציה (Swift, Kotlin, Dart).
- תיעוד אינטגרציה והגדרות.
- הוראות להוספת פילטרים חדשים.
- חודש תמיכה לאחר המסירה.
- בדיקות על 3+ מכשירים אמיתיים.
לוח זמנים ואחריות
5–7 ימי עסקים: פיתוח shaders עבור 5–8 פילטרים, אינטגרציה של תצוגה מקדימה וייצוא ב-iOS ואנדרואיד. גרסת Flutter עם קוד מקורי מוסיפה יומיים. העלות מחושבת באופן אישי—צרו קשר להערכה. אנו מבטיחים פעילות יציבה של הפילטרים במכשירים מ-iPhone 8 ואנדרואיד 10. הניסיון שלנו: 5+ שנים בפיתוח מובייל, 30+ פרויקטים של עיבוד וידאו. צרו קשר—נעזור ליישם פילטרי וידאו לאפליקציה שלכם.







