ממשקי API לניהול מאגרי נתונים של מצלמה HAL3

ב-Android 10 הוצגו ממשקי API אופציונליים לניהול מאגרים של camera HAL3, שמאפשרים לכם להטמיע לוגיקה של ניהול מאגרים כדי להשיג פשרות שונות בין זיכרון לבין זמן האחזור של הצילום בהטמעות של camera HAL.

ה-HAL של המצלמה דורש ש-N בקשות (כאשר N שווה לעומק צינור העיבוד) יוכנסו לתור בצינור העיבוד שלו, אבל לרוב הוא לא דורש את כל N קבוצות של מאגרי פלט בו-זמנית.

לדוגמה, יכול להיות ש-HAL יכלול שמונה בקשות בתור בצינור, אבל הוא יצטרך מאגרי פלט רק לשתי הבקשות בשלבים האחרונים של הצינור. במכשירים עם Android 9 ומטה, מסגרת המצלמה מקצה מאגרי נתונים זמניים כשהבקשה מתווספת לתור ב-HAL, כך שיכולים להיות שישה סטים של מאגרי נתונים זמניים ב-HAL שלא נמצאים בשימוש. ב-Android 10, ממשקי ה-API לניהול מאגרי הנתונים של HAL3 במצלמה מאפשרים להפריד את מאגרי הנתונים של הפלט כדי לפנות את שש קבוצות מאגרי הנתונים. השימוש ב-API הזה יכול לחסוך מאות מגה-בייט של זיכרון במכשירים מתקדמים, והוא יכול להיות שימושי גם במכשירים עם זיכרון מוגבל.

איור 1 מציג דיאגרמה של ממשק HAL של המצלמה במכשירים עם Android מגרסה 9 ומגרסאות קודמות. איור 2 מציג את ממשק HAL של המצלמה ב-Android 10 עם ממשקי ה-API של HAL3 לניהול מאגרים שהוטמעו.

ניהול מאגרים בגרסה 9 ומטה

איור 1. ממשק Camera HAL ב-Android מגרסה 9 ומטה

ניהול מאגרים ב-Android 10

איור 2. ממשק Camera HAL ב-Android 10 באמצעות ממשקי ה-API לניהול מאגרים

הטמעה של ממשקי ה-API לניהול מאגרים

כדי להטמיע את ממשקי ה-API לניהול מאגרים, ה-HAL של המצלמה צריך:

רכיב HAL של המצלמה משתמש בשיטות requestStreamBuffers ו-returnStreamBuffers ב-ICameraDeviceCallback.hal כדי לבקש ולשלוח מאגרי נתונים זמניים. בנוסף, רכיב ה-HAL צריך להטמיע את השיטה signalStreamFlush ב-ICameraDeviceSession.hal כדי לסמן לרכיב ה-HAL של המצלמה להחזיר מאגרי נתונים זמניים.

requestStreamBuffers

משתמשים בשיטה requestStreamBuffers כדי לבקש מאגרי נתונים ממסגרת המצלמה. כשמשתמשים בממשקי ה-API לניהול מאגרים של Camera HAL3, בקשות הצילום מ-Camera Framework לא מכילות מאגרי פלט, כלומר השדה bufferId ב-StreamBuffer הוא 0. לכן, רכיב HAL של המצלמה חייב להשתמש ב-requestStreamBuffers כדי לבקש מאגרי נתונים ממסגרת המצלמה.

השיטה requestStreamBuffers מאפשרת למתקשר לבקש מספר מאגרי נתונים ממספר זרמי פלט בקריאה אחת, וכך להקטין את מספר הקריאות ל-HIDL IPC. עם זאת, אם מבקשים כמה באפרים בו-זמנית, השיחות יימשכו יותר זמן, וזה עלול להשפיע לרעה על זמן האחזור הכולל מהבקשה ועד לקבלת התוצאה. בנוסף, מכיוון שהקריאות אל requestStreamBuffers עוברות סדרות בשרות המצלמה, מומלץ שרכיב HAL של המצלמה ישתמש בשרשור ייעודי בעדיפות גבוהה כדי לבקש מאגרי נתונים.

אם בקשה למאגר נכשלת, ה-HAL של המצלמה צריך להיות מסוגל לטפל כראוי בשגיאות לא קריטיות. הרשימה הבאה מתארת סיבות נפוצות לכשלים בבקשות של מאגרים, ומסבירה איך צריך לטפל בהן בשכבת HAL של המצלמה.

  • האפליקציה מתנתקת מזרם הפלט: זו שגיאה לא קריטית. רכיב HAL של המצלמה צריך לשלוח את הערך ERROR_REQUEST לכל בקשת צילום שמכוונת לזרם מנותק, ולהיות מוכן לעבד בקשות עוקבות בצורה רגילה.
  • זמן קצוב לתפוגה: יכול להיות שהאפליקציה עסוקה בעיבוד אינטנסיבי בזמן שהיא מחזיקה כמה מאגרי נתונים זמניים. ה-HAL של המצלמה צריך לשלוח ERROR_REQUEST לבקשות צילום שלא ניתן למלא בגלל שגיאת זמן קצוב לתפוגה, ולהיות מוכן לעבד בקשות עוקבות כרגיל.
  • מסגרת המצלמה מכינה הגדרה חדשה של סטרימינג: ה-HAL של המצלמה צריך להמתין עד ששיחת configureStreams הבאה תושלם לפני שהוא קורא שוב ל-requestStreamBuffers.
  • ה-HAL של המצלמה הגיע למגבלת מאגר הנתונים הזמני (השדה maxBuffers): ה-HAL של המצלמה צריך להמתין עד שהוא מחזיר לפחות מאגר נתונים זמני אחד של הזרם לפני שהוא קורא שוב ל-requestStreamBuffers.

returnStreamBuffers

משתמשים בשיטה returnStreamBuffers כדי להחזיר מאגרי נתונים זמניים נוספים למסגרת המצלמה. בדרך כלל, רכיב HAL של המצלמה מחזיר מאגרי נתונים למסגרת המצלמה באמצעות השיטה processCaptureResult, אבל הוא יכול להתייחס רק לבקשות צילום שנשלחו לרכיב HAL של המצלמה. בשיטה requestStreamBuffers, יכול להיות שהטמעת ה-HAL של המצלמה תשמור יותר מאגרים ממה שמסגרת המצלמה ביקשה. במקרים כאלה צריך להשתמש בשיטה returnStreamBuffers. אם הטמעת ה-HAL אף פעם לא מחזיקה יותר מאגרים מהכמות שנדרשה, הטמעת ה-HAL של המצלמה לא צריכה לקרוא לשיטה returnStreamBuffers.

signalStreamFlush

השיטה signalStreamFlush מופעלת על ידי מסגרת המצלמה כדי להודיע ל-HAL של המצלמה להחזיר את כל המאגרים שזמינים. הפונקציה הזו נקראת בדרך כלל כשהמסגרת של המצלמה עומדת לקרוא ל-configureStreams, והיא צריכה לנקות את צינור הנתונים של לכידת המצלמה. בדומה לשיטה returnStreamBuffers אם הטמעה של HAL של מצלמה לא מחזיקה יותר מאגרים מהנדרש, יכול להיות שההטמעה של השיטה הזו תהיה ריקה.

אחרי ש-framework המצלמה קורא ל-signalStreamFlush, ה-framework מפסיק לשלוח בקשות חדשות לצילום ל-HAL של המצלמה עד שכל המאגרים מוחזרים ל-framework של המצלמה. כשכל המאגרים מוחזרים, קריאות השיטה requestStreamBuffers נכשלות ומסגרת המצלמה יכולה להמשיך את העבודה במצב נקי. לאחר מכן, מסגרת המצלמה קוראת לשיטה configureStreams או לשיטה processCaptureRequest. אם מסגרת המצלמה קוראת לשיטה configureStreams, ה-HAL של המצלמה יכול להתחיל לבקש מאגרי נתונים שוב אחרי שהקריאה ל-configureStreams חוזרת בהצלחה. אם מסגרת המצלמה קוראת לשיטה processCaptureRequest,‏ HAL של המצלמה יכול להתחיל לבקש מאגרי נתונים במהלך הקריאה ל-processCaptureRequest.

הסמנטיקה שונה בין השיטה signalStreamFlush לבין השיטה flush. כשמפעילים את השיטה flush, ממשק HAL יכול לבטל בקשות צילום בהמתנה באמצעות ERROR_REQUEST כדי לנקות את צינור הנתונים בהקדם האפשרי. כשמפעילים את השיטה signalStreamFlush, שכבת ה-HAL צריכה לסיים את כל בקשות הצילום שממתינות כרגיל ולהחזיר את כל המאגרים למסגרת המצלמה.

הבדל נוסף בין ה-method‏ signalStreamFlush לבין שיטות אחרות הוא ש-signalStreamFlush היא שיטת HIDL חד-כיוונית, כלומר יכול להיות שפריימוורק המצלמה יקרא לממשקי API חוסמים אחרים לפני ששכבת ה-HAL תקבל את הקריאה ל-signalStreamFlush. המשמעות היא שה-method‏ signalStreamFlush ו-methods אחרים (במיוחד ה-method‏ configureStreams) עשויים להגיע ל-HAL של המצלמה בסדר שונה מהסדר שבו הם נקראו במסגרת המצלמה. כדי לפתור את בעיית האסינכרוניות הזו, השדה streamConfigCounter נוסף ל-StreamConfiguration והתווסף כארגומנט לשיטה signalStreamFlush. ההטמעה של רכיב HAL של המצלמה צריכה להשתמש בארגומנט streamConfigCounter כדי לקבוע אם קריאה ל-signalStreamFlush מגיעה אחרי הקריאה התואמת ל-configureStreams. דוגמה מופיעה באיור 3.

טיפול בשיחות שמגיעות מאוחר

איור 3. איך ה-HAL של המצלמה צריך לזהות ולטפל בקריאות signalStreamFlush שמגיעות באיחור

שינויים בהתנהגות כשמטמיעים את ממשקי ה-API לניהול מאגרים

כשמשתמשים בממשקי ה-API לניהול מאגרים כדי להטמיע את הלוגיקה של ניהול מאגרים, צריך לקחת בחשבון את השינויים האפשריים הבאים בהתנהגות של המצלמה ושל הטמעת רכיב HAL של המצלמה:

  • בקשות הצילום מגיעות ל-HAL של המצלמה מהר יותר ובתדירות גבוהה יותר: בלי ממשקי API לניהול מאגרים, מסגרת המצלמה מבקשת מאגרי פלט לכל בקשת צילום לפני שהיא שולחת בקשת צילום ל-HAL של המצלמה. כשמשתמשים בממשקי ה-API לניהול מאגרים, מסגרת המצלמה לא צריכה יותר לחכות למאגרים, ולכן היא יכולה לשלוח בקשות צילום לרכיב HAL של המצלמה מוקדם יותר.

    בנוסף, בלי ממשקי API לניהול מאגרים, מסגרת המצלמה מפסיקה לשלוח בקשות צילום אם אחד מזרמי הפלט של בקשת הצילום הגיע למספר המאגרים המקסימלי ש-HAL יכול להכיל בכל פעם (הערך הזה מוגדר על ידי HAL של המצלמה בשדה HalStream::maxBuffers בערך ההחזרה של קריאה ל-configureStreams). עם ממשקי ה-API לניהול מאגרים, התנהגות ההגבלה הזו כבר לא קיימת, וההטמעה של HAL המצלמה לא יכולה לקבל קריאות של processCaptureRequest כשיש יותר מדי בקשות ללכידה בתור של HAL.

  • requestStreamBuffers זמן האחזור של השיחות משתנה באופן משמעותי: יש הרבה סיבות לכך שrequestStreamBuffersשיחה עשויה להימשך זמן רב יותר מהממוצע. לדוגמה:

    • יכול להיות שהקריאות לכמה מאגרי הנתונים הראשונים של שידור שנוצר לאחרונה יימשכו זמן רב יותר, כי המכשיר צריך להקצות זיכרון.
    • החביון הצפוי גדל ביחס למספר המאגרים שנדרשים בכל קריאה.
    • האפליקציה מחזיקה במאגרי נתונים זמניים ועסוקה בעיבוד. הדבר עלול לגרום לבקשות של מאגרים לפעול לאט יותר או להגיע לזמן קצוב לתפוגה בגלל חוסר במאגרים או בגלל עומס על המעבד.

אסטרטגיות לניהול מאגרים

ממשקי ה-API לניהול מאגרים מאפשרים להטמיע סוגים שונים של אסטרטגיות לניהול מאגרים. לפניכם מספר דוגמאות:

  • תאימות לאחור: שכבת ה-HAL מבקשת מאגרי נתונים זמניים לבקשת צילום במהלך הקריאה ל-processCaptureRequest. השיטה הזו לא חוסכת בזיכרון, אבל היא יכולה לשמש כיישום הראשון של ממשקי ה-API לניהול מאגרים, והיא דורשת מעט מאוד שינויים בקוד של ה-HAL הקיים של המצלמה.
  • חיסכון מקסימלי בזיכרון: שכבת ה-HAL של המצלמה מבקשת מאגרי פלט רק לפני שצריך למלא אותם. השיטה הזו מאפשרת חיסכון מקסימלי בזיכרון. החיסרון הפוטנציאלי הוא יותר בעיות בממשק (jank) בצינור העברת הנתונים של המצלמה, אם בקשות המאגר נמשכות זמן רב באופן חריג.
  • במטמון: ה-HAL של המצלמה שומר במטמון כמה מאגרי נתונים זמניים, כדי להקטין את הסיכוי שמאגר נתונים זמני איטי מדי ישפיע על הביצועים.

רכיב HAL של המצלמה יכול לאמץ אסטרטגיות שונות לתרחישי שימוש ספציפיים. לדוגמה, הוא יכול להשתמש באסטרטגיה של חיסכון מקסימלי בזיכרון לתרחישי שימוש שצורכים הרבה זיכרון, ובאסטרטגיה של תאימות לאחור לתרחישי שימוש אחרים.

דוגמה להטמעה ב-HAL של מצלמה חיצונית

ממשק HAL של מצלמה חיצונית הוצג ב-Android 9, ואפשר למצוא אותו בעץ המקור בכתובת hardware/interfaces/camera/device/3.5/. ב-Android 10, הוא עודכן כך שיכלול את ExternalCameraDeviceSession.cpp, הטמעה של ה-API לניהול מאגרים. ‫HAL של המצלמה החיצונית הזו מיישם את אסטרטגיית החיסכון המקסימלית בזיכרון שמוזכרת באסטרטגיות לניהול מאגרים בכמה מאות שורות של קוד C++.