ממשקי 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. ממשק HAL של המצלמה ב-Android 10 באמצעות ממשקי ה-API לניהול מאגרים

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

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

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

requestStreamBuffers

משתמשים בשיטה requestStreamBuffers כדי לבקש מאגרי נתונים ממסגרת המצלמה. כשמשתמשים בממשקי ה-API לניהול מאגרים של HAL3 של המצלמה, בקשות הצילום ממסגרת המצלמה לא מכילות מאגרי פלט, כלומר השדה 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 חד-כיווני, כלומר יכול להיות שקוד ה-framework של המצלמה יקרא לממשקי 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 של המצלמה מבקש מאגרי פלט רק לפני שצריך למלא אותם. השיטה הזו מאפשרת חיסכון מקסימלי בזיכרון. החיסרון הפוטנציאלי הוא יותר באגים בצינור המצלמה כשבקשות למאגר נמשכות זמן רב באופן חריג.
  • במטמון: ה-HAL של המצלמה שומר במטמון כמה מאגרי נתונים זמניים, כדי שסביר פחות שמאגרי נתונים זמניים יושפעו מבקשות איטיות מדי פעם.

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

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

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