מצלמת רכב HAL

אנדרואיד מכיל שכבת HIDL Hardware Abstraction Layer (HAL) לרכב המספקת לכידת תמונות והצגה בשלב מוקדם מאוד בתהליך האתחול של אנדרואיד וממשיכה לתפקד למשך כל חיי המערכת. ה-HAL כולל את ערימת מערכת התצוגה החיצונית (EVS) ומשמשת בדרך כלל לתמיכה במצלמות אחוריות ובתצוגות תצוגה היקפית בכלי רכב עם מערכות אינפורמציה לרכב (IVI) מבוססות אנדרואיד. EVS גם מאפשר ליישם תכונות מתקדמות באפליקציות משתמש.

אנדרואיד כולל גם ממשק מנהל התקן לכידה ותצוגה ספציפי ל-EVS (ב /hardware/interfaces/automotive/evs/1.0 /evs/1.0). אמנם ניתן לבנות אפליקציית מצלמה אחורית על גבי שירותי המצלמה והתצוגה הקיימים של אנדרואיד, אך סביר להניח שאפליקציה כזו תפעל מאוחר מדי בתהליך האתחול של אנדרואיד. שימוש ב-HAL ייעודי מאפשר ממשק יעיל ומבהיר מה יצרן OEM צריך ליישם כדי לתמוך בערימת EVS.

רכיבי מערכת

EVS כולל את רכיבי המערכת הבאים:

דיאגרמת רכיבי מערכת EVS

איור 1. סקירת רכיבי מערכת EVS.

אפליקציית EVS

אפליקציית C++ EVS לדוגמה ( /packages/services/Car/evs/app ) משמשת כיישום ייחוס. אפליקציה זו אחראית לבקשת מסגרות וידאו מ-EVS Manager ושליחת פריימים מוגמרים לתצוגה בחזרה ל-EVS Manager. זה מצפה להיות מופעל על ידי init ברגע ש-EVS ושירות רכב יהיו זמינים, ממוקד תוך שתי (2) שניות מההפעלה. יצרני OEM יכולים לשנות או להחליף את אפליקציית EVS לפי הצורך.

מנהל EVS

מנהל ה-EVS ( /packages/services/Car/evs/manager ) מספק את אבני הבניין הדרושים לאפליקציית EVS כדי ליישם כל דבר, החל מתצוגה פשוטה של ​​מצלמה אחורית ועד לעיבוד מרובות מצלמות 6DOF. הממשק שלה מוצג באמצעות HIDL והוא בנוי לקבל מספר לקוחות במקביל. אפליקציות ושירותים אחרים (במיוחד שירות הרכב) יכולים לשאול את מצב מנהל EVS כדי לגלות מתי מערכת ה-EVS פעילה.

ממשק EVS HIDL

מערכת ה-EVS, הן המצלמה והן רכיבי התצוגה, מוגדרת בחבילת android.hardware.automotive.evs . יישום לדוגמה שמפעיל את הממשק (יוצר תמונות בדיקה סינתטיות ומאמת את התמונות בנסיעה הלוך ושוב) מסופק ב- /hardware/interfaces/automotive/evs/1.0/default .

ה-OEM אחראי להטמעת ה-API המובע על ידי קבצי ה-hal ב- /hardware/interfaces/automotive/evs . יישומים כאלה אחראים על קביעת התצורה ואיסוף הנתונים ממצלמות פיזיות ומסירתם באמצעות מאגרי זיכרון משותפים הניתנים לזיהוי על ידי Gralloc. צד התצוגה של היישום אחראי על אספקת מאגר זיכרון משותף שניתן למלא על ידי האפליקציה (בדרך כלל באמצעות עיבוד EGL) והצגת הפריימים המוגמרים בהעדפה לכל דבר אחר שאולי ירצה להופיע בתצוגה הפיזית. הטמעות של ספקים של ממשק EVS עשויות להיות מאוחסנות תחת /vendor/… /device/… או hardware/… (למשל, /hardware/[vendor]/[platform]/evs ).

נהגי ליבה

מכשיר התומך בערימת EVS דורש מנהלי התקנים של ליבה. במקום ליצור מנהלי התקנים חדשים, ליצרני ציוד מקורי יש אפשרות לתמוך בתכונות הנדרשות ל-EVS באמצעות מנהלי התקנים קיימים של מצלמה ו/או תצוגה. שימוש חוזר במנהלי התקנים יכול להיות יתרון, במיוחד עבור מנהלי התקנים לתצוגה שבהם הצגת תמונה עשויה לדרוש תיאום עם שרשורים פעילים אחרים. אנדרואיד 8.0 כולל מנהל התקן לדוגמה מבוסס v4l2 ( packages/services/Car/evs/sampleDriver ) התלוי בקרנל לתמיכה ב-v4l2 וב-SurfaceFlinger להצגת תמונת הפלט.

תיאור ממשק החומרה של EVS

הסעיף מתאר את ה-HAL. הספקים צפויים לספק יישומים של API זה המותאמים לחומרה שלהם.

IEvsEnumerator

אובייקט זה אחראי על ספירת חומרת ה-EVS הזמינה במערכת (מצלמה אחת או יותר והתקן התצוגה היחיד).

getCameraList() generates (vec<CameraDesc> cameras);

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

openCamera(string camera_id) generates (IEvsCamera camera);

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

closeCamera(IEvsCamera camera);

משחרר את ממשק IEvsCamera (והוא ההפך מהקריאה של openCamera() ). יש לעצור את זרם הווידאו של המצלמה על ידי קריאה ל- stopVideoStream() לפני קריאת closeCamera .

openDisplay() generates (IEvsDisplay display);

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

closeDisplay(IEvsDisplay display);

משחרר את ממשק IEvsDisplay (והוא ההפך מהקריאה openDisplay() ). יש להחזיר מאגרים יוצאי דופן שהתקבלו באמצעות קריאות getTargetBuffer() לתצוגה לפני סגירת התצוגה.

getDisplayState() generates (DisplayState state);

מקבל את מצב התצוגה הנוכחי. יישום HAL צריך לדווח על המצב הנוכחי בפועל, שעשוי להיות שונה מהמצב המבוקש האחרון. ההיגיון האחראי לשינוי מצבי תצוגה צריך להתקיים מעל שכבת המכשיר, מה שהופך אותו לבלתי רצוי שיישום HAL ישנה באופן ספונטני מצבי תצוגה. אם התצוגה אינה מוחזקת כעת על ידי אף לקוח (על ידי קריאה ל-openDisplay), פונקציה זו מחזירה NOT_OPEN . אחרת, הוא מדווח על המצב הנוכחי של EVS Display (ראה IEvsDisplay API ).

struct CameraDesc {
    string      camera_id;
    int32       vendor_flags;       // Opaque value
}
  • camera_id . מחרוזת המזהה באופן ייחודי מצלמה נתונה. יכול להיות שם מכשיר הליבה של ההתקן או שם למכשיר, כגון ראייה לאחור . הערך עבור מחרוזת זו נבחר על ידי יישום HAL ומשמש בצורה אטומה על ידי המחסנית שלמעלה.
  • vendor_flags . שיטה להעברת מידע מצלמה מיוחד בצורה אטומה מהנהג לאפליקציית EVS מותאמת אישית. זה מועבר ללא פירוש מהנהג עד לאפליקציית EVS, שהיא חופשית להתעלם ממנה.

IEvsCamera

אובייקט זה מייצג מצלמה בודדת והוא הממשק העיקרי ללכידת תמונות.

getCameraInfo() generates (CameraDesc info);

מחזירה CameraDesc של מצלמה זו.

setMaxFramesInFlight(int32 bufferCount) generates (EvsResult result);

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

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

startVideoStream(IEvsCameraStream receiver) generates (EvsResult result);

מבקש משלוח מסגרות למצלמות EVS ממצלמה זו. ה-IEvsCameraStream מתחיל לקבל שיחות תקופתיות עם מסגרות תמונה חדשות עד שנקרא stopVideoStream() . מסגרות חייבות להתחיל להיות מסופקות בתוך 500 אלפיות השנייה מהשיחה startVideoStream ולאחר ההתחלה, חייבות להיווצר במינימום של 10 FPS. הזמן הדרוש להפעלת זרם הווידאו נספר ביעילות כנגד כל דרישה לזמן ההפעלה של המצלמה האחורית. אם הזרם לא מופעל, יש להחזיר קוד שגיאה; אחרת אישור מוחזר.

oneway doneWithFrame(BufferDesc buffer);

מחזירה מסגרת שנמסרה על ידי ל-IEvsCameraStream. בסיום צריכת מסגרת שנמסרה לממשק IEvsCameraStream, יש להחזיר את המסגרת ל-IEvsCamera לשימוש חוזר. מספר קטן וסופי של מאגרים זמין (אולי קטן כמו אחד), ואם האספקה ​​נגמרת, לא יימסרו מסגרות נוספות עד להחזרת מאגר, מה שעלול לגרום לפריימים שדילגו (מאגר עם ידית אפס מציינת את הסוף של זרם ואין צורך להחזיר אותו דרך פונקציה זו). מחזיר אישור עם הצלחה, או קוד שגיאה מתאים, עשוי לכלול INVALID_ARG או BUFFER_NOT_AVAILABLE .

stopVideoStream();

מפסיק את האספקה ​​של מסגרות למצלמות EVS. מכיוון שהמסירה היא אסינכרונית, מסגרות עשויות להמשיך להגיע למשך זמן מה לאחר חזרת השיחה הזו. יש להחזיר כל פריים עד לסגירת הזרם לאותת ל-IEvsCameraStream. זה חוקי להתקשר stopVideoStream בזרם שכבר הופסק או מעולם לא התחיל, ובמקרים זה מתעלם.

getExtendedInfo(int32 opaqueIdentifier) generates (int32 value);

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

setExtendedInfo(int32 opaqueIdentifier, int32 opaqueValue) generates (EvsResult result);

שולח ערך ספציפי לנהג למימוש HAL. הרחבה זו ניתנת רק כדי להקל על הרחבות ספציפיות לרכב ושום יישום HAL לא צריך לדרוש קריאה זו לפעול במצב ברירת מחדל. אם הנהג מזהה ומקבל את הערכים, יש להחזיר את OK; אחרת יש להחזיר INVALID_ARG או קוד שגיאה מייצג אחר.

struct BufferDesc {
    uint32  width;      // Units of pixels
    uint32  height;     // Units of pixels
    uint32  stride;     // Units of pixels
    uint32  pixelSize;  // Size of single pixel in bytes
    uint32  format;     // May contain values from android_pixel_format_t
    uint32  usage;      // May contain values from Gralloc.h
    uint32  bufferId;   // Opaque value
    handle  memHandle;  // gralloc memory buffer handle
}

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

  • width . הרוחב בפיקסלים של התמונה המוצגת.
  • height . הגובה בפיקסלים של התמונה המוצגת.
  • stride . מספר הפיקסלים שכל שורה תופסת למעשה בזיכרון, מה שאחראי על כל ריפוד ליישור שורות. מבוטא בפיקסלים כדי להתאים למוסכמה שאומצה על ידי gralloc עבור תיאורי המאגר שלה.
  • pixelSize . מספר הבתים שנכבשו על ידי כל פיקסל בודד, המאפשר חישוב של הגודל בבייטים הדרוש למעבר בין שורות בתמונה ( stride בבתים = stride בפיקסלים * pixelSize ).
  • format . פורמט הפיקסלים המשמש את התמונה. הפורמט המסופק חייב להיות תואם למימוש OpenGL של הפלטפורמה. כדי לעבור בדיקות תאימות, יש להעדיף HAL_PIXEL_FORMAT_YCRCB_420_SP לשימוש במצלמה, ויש להעדיף RGBA או BGRA לתצוגה.
  • usage . דגלי שימוש שנקבעו על ידי יישום HAL. לקוחות HAL צפויים להעביר אותם ללא שינוי (לפרטים, עיין בדגלים הקשורים ל- Gralloc.h ).
  • bufferId . ערך ייחודי שצוין על ידי יישום HAL כדי לאפשר זיהוי מאגר לאחר נסיעה הלוך ושוב דרך ממשקי ה-API של HAL. הערך המאוחסן בשדה זה עשוי להיבחר באופן שרירותי על ידי יישום HAL.
  • memHandle . הידית למאגר הזיכרון הבסיסי המכיל את נתוני התמונה. מימוש ה-HAL עשוי לבחור לאחסן כאן ידית מאגר של Gralloc.

IEvsCameraStream

הלקוח מיישם ממשק זה כדי לקבל משלוחי פריימים אסינכרוניים של וידאו.

deliverFrame(BufferDesc buffer);

מקבל שיחות מה-HAL בכל פעם שמסגרת וידאו מוכנה לבדיקה. יש להחזיר את נקודות האחיזה למאגר המתקבלות בשיטה זו באמצעות קריאות ל- IEvsCamera::doneWithFrame() . כאשר זרם הווידאו נעצר באמצעות קריאה ל- IEvsCamera::stopVideoStream() , התקשרות חזרה זו עשויה להימשך כשהצינור מתנקז. עדיין יש להחזיר כל מסגרת; כאשר הפריים האחרון בזרם נמסר, יימסר NULL bufferHandle, המסמל את סוף הזרם ולא מתרחשות מסירות פריים נוספות. ה-NULL bufferHandle עצמו לא צריך להישלח בחזרה דרך doneWithFrame() , אבל יש להחזיר את כל שאר הנקודות.

בעוד פורמטים קנייניים של מאגר אפשריים מבחינה טכנית, בדיקת התאימות דורשת שהמאגר יהיה באחד מארבעה פורמטים נתמכים: NV21 (YCrCb 4:2:0 Semi-Planar), YV12 (YCrCb 4:2:0 Planar), YUYV (YCrCb 4: 2:2 Interleaved), RGBA (32 סיביות R:G:B:x), BGRA (32 סיביות B:G:R:x). הפורמט הנבחר חייב להיות מקור טקסטור GL חוקי ביישום GLES של הפלטפורמה.

האפליקציה לא צריכה להסתמך על שום התכתבות בין שדה bufferId לבין memHandle במבנה BufferDesc . ערכי bufferId הם בעצם פרטיים למימוש מנהל ההתקן של HAL, והוא עשוי להשתמש בהם (ולהשתמש בהם מחדש) כראות עיניו.

IEvsDisplay

אובייקט זה מייצג את תצוגת ה-EVS, שולט במצב התצוגה ומטפל בהצגת התמונות בפועל.

getDisplayInfo() generates (DisplayDesc info);

מחזיר מידע בסיסי על תצוגת EVS המסופקת על ידי המערכת (ראה DisplayDesc ).

setDisplayState(DisplayState state) generates (EvsResult result);

מגדיר את מצב התצוגה. לקוחות יכולים להגדיר את מצב התצוגה כדי לבטא את המצב הרצוי, והטמעת HAL חייבת לקבל בחינניות בקשה עבור כל מדינה בכל מצב אחר, אם כי התגובה עשויה להיות התעלמות מהבקשה.

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

זה תקף לכל מדינה להתבקש בכל עת. אם התצוגה כבר גלויה, היא אמורה להישאר גלויה אם היא מוגדרת ל- VISIBLE_ON_NEXT_FRAME . תמיד מחזיר אישור אלא אם כן המצב המבוקש הוא ערך enum לא מזוהה, ובמקרה זה INVALID_ARG מוחזר.

getDisplayState() generates (DisplayState state);

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

getTargetBuffer() generates (handle bufferHandle);

מחזיר נקודת אחיזה למאגר מסגרת המשויך לתצוגה. מאגר זה עשוי להיות נעול ולכתוב אליו באמצעות תוכנה ו/או GL. יש להחזיר את המאגר הזה באמצעות קריאה ל- returnTargetBufferForDisplay() גם אם התצוגה אינה גלויה יותר.

בעוד פורמטים קנייניים של מאגר אפשריים מבחינה טכנית, בדיקת התאימות דורשת שהמאגר יהיה באחד מארבעה פורמטים נתמכים: NV21 (YCrCb 4:2:0 Semi-Planar), YV12 (YCrCb 4:2:0 Planar), YUYV (YCrCb 4: 2:2 Interleaved), RGBA (32 סיביות R:G:B:x), BGRA (32 סיביות B:G:R:x). הפורמט שנבחר חייב להיות יעד עיבוד GL חוקי ביישום GLES של הפלטפורמה.

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

returnTargetBufferForDisplay(handle bufferHandle) generates (EvsResult result);

אומר לתצוגה שהמאגר מוכן לתצוגה. רק מאגרים שאוחזרו באמצעות קריאה ל- getTargetBuffer() תקפים לשימוש עם קריאה זו, ויתכן שהתוכן של BufferDesc לא ישונה על ידי אפליקציית הלקוח. לאחר שיחה זו, המאגר אינו תקף יותר לשימוש על ידי הלקוח. מחזיר אישור עם הצלחה, או קוד שגיאה מתאים, עשוי לכלול INVALID_ARG או BUFFER_NOT_AVAILABLE .

struct DisplayDesc {
     string  display_id;
     int32   vendor_flags;  // Opaque value
}

מתאר את המאפיינים הבסיסיים של תצוגת EVS והנדרשים על ידי יישום EVS. ה-HAL אחראי למילוי מבנה זה כדי לתאר את תצוגת EVS. יכול להיות תצוגה פיזית או תצוגה וירטואלית שמכוסה או מעורבבת עם מכשיר מצגת אחר.

  • display_id . מחרוזת המזהה באופן ייחודי את התצוגה. זה יכול להיות שם מכשיר הליבה של ההתקן, או שם למכשיר, כגון Rearview . הערך עבור מחרוזת זו נבחר על ידי יישום HAL ומשמש בצורה אטומה על ידי המחסנית שלמעלה.
  • vendor_flags . שיטה להעברת מידע מצלמה מיוחד בצורה אטומה מהנהג לאפליקציית EVS מותאמת אישית. זה מועבר ללא פירוש מהנהג עד לאפליקציית EVS, שהיא חופשית להתעלם ממנה.
enum DisplayState : uint32 {
    NOT_OPEN,               // Display has not been “opened” yet
    NOT_VISIBLE,            // Display is inhibited
    VISIBLE_ON_NEXT_FRAME,  // Will become visible with next frame
    VISIBLE,                // Display is currently active
    DEAD,                   // Display is not available. Interface should be closed
}

מתאר את המצב של תצוגת EVS, אותה ניתן לבטל (לא גלוי לנהג) או להפעיל אותה (הצגה תמונה לנהג). כולל מצב חולף שבו התצוגה עדיין לא נראית אך מוכנה להפוך לגלויה עם אספקת המסגרת הבאה של התמונות באמצעות הקריאה returnTargetBufferForDisplay() .

מנהל EVS

מנהל EVS מספק את הממשק הציבורי למערכת EVS לאיסוף והצגת תצוגות מצלמות חיצוניות. כאשר מנהלי התקנים לחומרה מאפשרים רק ממשק פעיל אחד לכל משאב (מצלמה או תצוגה), מנהל ה-EVS מאפשר גישה משותפת למצלמות. אפליקציית EVS ראשית אחת היא הלקוח הראשון של EVS Manager, והיא הלקוח היחיד המורשה לכתוב נתוני תצוגה (ניתן להעניק ללקוחות נוספים גישת קריאה בלבד לתמונות מצלמה).

מנהל ה-EVS מיישם את אותו API כמו מנהלי ההתקן הבסיסיים של HAL ומספק שירות מורחב על ידי תמיכה במספר לקוחות במקביל (יותר מלקוח אחד יכול לפתוח מצלמה דרך מנהל ה-EVS ולקבל זרם וידאו).

דיאגרמת EVS Manager ו-EVS Hardware API.

איור 2. EVS Manager משקף את ה-API של החומרה של EVS.

אפליקציות אינן רואות הבדלים כאשר הן פועלות דרך יישום EVS Hardware HAL או EVS Manager API, מלבד העובדה שה-EVS Manager API מאפשר גישה לזרימת מצלמה במקביל. מנהל ה-EVS הוא, בעצמו, הלקוח המותר של שכבת EVS Hardware HAL, ופועל כפרוקסי עבור EVS Hardware HAL.

הסעיפים הבאים מתארים רק את אותן שיחות שיש להן התנהגות שונה (מורחבת) ביישום EVS Manager; השיחות הנותרות זהות לתיאורי EVS HAL.

IEvsEnumerator

openCamera(string camera_id) generates (IEvsCamera camera);

משיג אובייקט ממשק המשמש לאינטראקציה עם מצלמה ספציפית המזוהה על ידי מחרוזת ה-camera_id הייחודית. מחזירה NULL בעת כשל. בשכבת EVS Manager, כל עוד יש מספיק משאבי מערכת זמינים, מצלמה שכבר פתוחה עשויה להיפתח שוב על ידי תהליך אחר, מה שמאפשר העברת זרם הווידאו למספר אפליקציות צרכניות. מחרוזות camera_id בשכבת EVS Manager זהות לאלו שדווחו לשכבת EVS Hardware.

IEvsCamera

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

startVideoStream(IEvsCameraStream receiver) generates (EvsResult result);

מתחיל שידורי וידאו. לקוחות עשויים להפעיל ולהפסיק באופן עצמאי זרמי וידאו באותה מצלמה בסיסית. המצלמה הבסיסית מתחילה כשהלקוח הראשון מתחיל.

doneWithFrame(uint32 frameId, handle bufferHandle) generates (EvsResult result);

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

stopVideoStream();

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

setExtendedInfo(int32 opaqueIdentifier, int32 opaqueValue) generates (EvsResult result);

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

IEvsDisplay

רק בעלים אחד של התצוגה מותר, אפילו ברמת מנהל EVS. המנהל לא מוסיף שום פונקציונליות ופשוט מעביר את ממשק IEvsDisplay ישירות למימוש HAL הבסיסי.

אפליקציית EVS

אנדרואיד כולל יישום ייחוס מקורי של C++ של אפליקציית EVS המתקשרת עם EVS Manager ו-Vehicle HAL כדי לספק פונקציות בסיסיות של מצלמה אחורית. האפליקציה צפויה להתחיל מוקדם מאוד בתהליך האתחול של המערכת, כאשר סרטון מתאים יוצג בהתאם למצלמות הזמינות ולמצב המכונית (מצב גיר ואותת פנייה). יצרני OEM יכולים לשנות או להחליף את אפליקציית EVS בהיגיון ובמצגת הספציפיים לרכב משלהם.

איור 3. לוגיקה לדוגמה של אפליקציית EVS, קבל רשימת מצלמות.



איור 4. לוגיקה לדוגמה של אפליקציית EVS, קבלת התקשרות חוזרת במסגרת.

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

לדוגמה, האפליקציה עשויה לבחור להעביר את נתוני הפיקסלים בעצמה, פוטנציאלית באמצעות קנה מידה מוטבע או פעולת סיבוב. האפליקציה יכולה גם לבחור להשתמש בתמונת המקור כמרקם OpenGL ולעבד סצנה מורכבת למאגר הפלט, כולל אלמנטים וירטואליים כגון אייקונים, קווים מנחים ואנימציות. אפליקציה מתוחכמת יותר עשויה גם לבחור במספר מצלמות קלט בו-זמנית ולמזג אותן למסגרת הפלט הבודדת (כגון לשימוש בתצוגה וירטואלית מלמעלה למטה של ​​סביבת הרכב).

השתמש ב-EGL/SurfaceFlinger ב-EVS Display HAL

סעיף זה מסביר כיצד להשתמש ב-EGL לעיבוד יישום EVS Display HAL באנדרואיד 10.

מימוש ייחוס של EVS HAL משתמש ב-EGL כדי להציג את התצוגה המקדימה של המצלמה על המסך ומשתמש ב- libgui כדי ליצור את משטח עיבוד ה-EGL של היעד. באנדרואיד 8 (ומעלה), libgui מסווג כ- VNDK-private , המתייחס לקבוצת ספריות הזמינות לספריות VNDK שתהליכי הספק לא יכולים להשתמש בהן. מכיוון שהטמעות HAL חייבות להימצא במחיצת הספק, מנועים מהספקים להשתמש ב-Surface ביישומי HAL.

בניית libgui עבור תהליכי ספקים

השימוש ב- libgui משמש כאפשרות היחידה להשתמש ב-EGL/SurfaceFlinger ביישומי EVS Display HAL. הדרך הפשוטה ביותר ליישם libgui היא באמצעות frameworks/native/libs/gui ישירות על ידי שימוש ביעד בנייה נוסף בסקריפט ה-build. יעד זה זהה לחלוטין ליעד libgui למעט תוספת של שני שדות:

  • name
  • vendor_available
cc_library_shared {
    name: "libgui_vendor",
    vendor_available: true,
    vndk: {
        enabled: false,
    },
    double_loadable: true,

defaults: ["libgui_bufferqueue-defaults"],
srcs: [ … // bufferhub is not used when building libgui for vendors target: { vendor: { cflags: [ "-DNO_BUFFERHUB", "-DNO_INPUT", ], …

הערה: יעדי ספקים בנויים עם המאקרו NO_INPUT , אשר מסיר מילה אחת של 32 סיביות מנתוני החבילה. מכיוון ש- SurfaceFlinger מצפה לשדה זה שהוסר, SurfaceFlinger לא מצליח לנתח את החבילה. זה נצפה ככשל fcntl :

W Parcel  : Attempt to read object from Parcel 0x78d9cffad8 at offset 428 that is not in the object list
E Parcel  : fcntl(F_DUPFD_CLOEXEC) failed in Parcel::read, i is 0, fds[i] is 0, fd_count is 20, error: Unknown error 2147483647
W Parcel  : Attempt to read object from Parcel 0x78d9cffad8 at offset 544 that is not in the object list

כדי לפתור מצב זה:

diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 6066421fa..25cf5f0ce 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -54,6 +54,9 @@ status_t layer_state_t::write(Parcel& output) const
     output.writeFloat(color.b);
 #ifndef NO_INPUT
     inputInfo.write(output);
+#else
+    // Write a dummy 32-bit word.
+    output.writeInt32(0);
 #endif
     output.write(transparentRegion);
     output.writeUint32(transform);

הוראות בנייה לדוגמה מסופקות להלן. צפו לקבל $(ANDROID_PRODUCT_OUT)/system/lib64/libgui_vendor.so .

$ cd <your_android_source_tree_top>
$ . ./build/envsetup.
$ lunch <product_name>-<build_variant>
============================================
PLATFORM_VERSION_CODENAME=REL
PLATFORM_VERSION=10
TARGET_PRODUCT=<product_name>
TARGET_BUILD_VARIANT=<build_variant>
TARGET_BUILD_TYPE=release
TARGET_ARCH=arm64
TARGET_ARCH_VARIANT=armv8-a
TARGET_CPU_VARIANT=generic
TARGET_2ND_ARCH=arm
TARGET_2ND_ARCH_VARIANT=armv7-a-neon
TARGET_2ND_CPU_VARIANT=cortex-a9
HOST_ARCH=x86_64
HOST_2ND_ARCH=x86
HOST_OS=linux
HOST_OS_EXTRA=<host_linux_version>
HOST_CROSS_OS=windows
HOST_CROSS_ARCH=x86
HOST_CROSS_2ND_ARCH=x86_64
HOST_BUILD_TYPE=release
BUILD_ID=QT
OUT_DIR=out
============================================

$ m -j libgui_vendor … $ find $ANDROID_PRODUCT_OUT/system -name "libgui_vendor*" .../out/target/product/hawk/system/lib64/libgui_vendor.so .../out/target/product/hawk/system/lib/libgui_vendor.so

השתמש בקלסר ביישום EVS HAL

באנדרואיד 8 (ומעלה), צומת מכשיר /dev/binder הפך בלעדי לתהליכי מסגרת ולכן, לא נגיש לתהליכי הספק. במקום זאת, תהליכי הספק צריכים להשתמש ב- /dev/hwbinder וחייבים להמיר את כל ממשקי ה-AIDL ל-HIDL. למי שרוצה להמשיך להשתמש בממשקי AIDL בין תהליכי הספק, השתמש בדומיין ה-binder, /dev/vndbinder .

דומיין IPC תיאור
/dev/binder IPC בין תהליכי מסגרת/אפליקציה עם ממשקי AIDL
/dev/hwbinder IPC בין תהליכי מסגרת/ספק עם ממשקי HIDL
IPC בין תהליכי ספק עם ממשקי HIDL
/dev/vndbinder IPC בין תהליכי ספק/ספק עם ממשקי AIDL

בעוד SurfaceFlinger מגדיר ממשקי AIDL, תהליכי ספק יכולים להשתמש רק בממשקי HIDL כדי לתקשר עם תהליכי מסגרת. נדרשת כמות עבודה לא טריוויאלית כדי להמיר ממשקי AIDL קיימים ל-HIDL. למרבה המזל, אנדרואיד מספקת שיטה שבה ניתן לבחור את מנהל ההתקן של ה-binder עבור libbinder , שאליו מקושרים תהליכי ספריית מרחב המשתמש.

diff --git a/evs/sampleDriver/service.cpp b/evs/sampleDriver/service.cpp
index d8fb3166..5fd02935 100644
--- a/evs/sampleDriver/service.cpp
+++ b/evs/sampleDriver/service.cpp
@@ -21,6 +21,7 @@
 #include <utils/Errors.h>
 #include <utils/StrongPointer.h>
 #include <utils/Log.h>
+#include <binder/ProcessState.h>

 #include "ServiceNames.h"
 #include "EvsEnumerator.h"
@@ -43,6 +44,9 @@ using namespace android;
 int main() {
     ALOGI("EVS Hardware Enumerator service is starting");


+    // Use /dev/binder for SurfaceFlinger
+    ProcessState::initWithDriver("/dev/binder");
+


     // Start a thread to listen to video device addition events.
     std::atomic<bool> running { true };
     std::thread ueventHandler(EvsEnumerator::EvsUeventThread, std::ref(running));

הערה: תהליכי ספק צריכים לקרוא לזה לפני קריאה ל- Process או IPCThreadState , או לפני ביצוע קריאות מקשר.

מדיניות SELinux

אם יישום ההתקן הוא טרבל מלא, SELinux מונע מתהליכי הספק להשתמש ב- /dev/binder . לדוגמה, יישום לדוגמה של EVS HAL מוקצה לתחום hal_evs_driver ודורש הרשאות r/w לתחום binder_device .

W ProcessState: Opening '/dev/binder' failed: Permission denied
F ProcessState: Binder driver could not be opened. Terminating.
F libc    : Fatal signal 6 (SIGABRT), code -1 (SI_QUEUE) in tid 9145 (android.hardwar), pid 9145 (android.hardwar)
W android.hardwar: type=1400 audit(0.0:974): avc: denied { read write } for name="binder" dev="tmpfs" ino=2208 scontext=u:r:hal_evs_driver:s0 tcontext=u:object_r:binder_device:s0 tclass=chr_file permissive=0

הוספת הרשאות אלה, לעומת זאת, גורמת לכשל בבנייה מכיוון שהיא מפרה את הכללים הבאים לעולם אל-לאפשר המוגדרים ב- system/sepolicy/domain.te עבור התקן מלא בטרבל.

libsepol.report_failure: neverallow on line 631 of system/sepolicy/public/domain.te (or line 12436 of policy.conf) violated by allow hal_evs_driver binder_device:chr_file { read write };
libsepol.check_assertions: 1 neverallow failures occurred
full_treble_only(`
  neverallow {
    domain
    -coredomain
    -appdomain
    -binder_in_vendor_violators
  } binder_device:chr_file rw_file_perms;
')

binder_in_vendor_violators היא תכונה המסופקת כדי לתפוס באג ולהדריך פיתוח. זה יכול לשמש גם כדי לפתור את ההפרה של Android 10 שתוארה לעיל.

diff --git a/evs/sepolicy/evs_driver.te b/evs/sepolicy/evs_driver.te
index f1f31e9fc..6ee67d88e 100644
--- a/evs/sepolicy/evs_driver.te
+++ b/evs/sepolicy/evs_driver.te
@@ -3,6 +3,9 @@ type hal_evs_driver, domain, coredomain;
 hal_server_domain(hal_evs_driver, hal_evs)
 hal_client_domain(hal_evs_driver, hal_evs)

+# Allow to use /dev/binder
+typeattribute hal_evs_driver binder_in_vendor_violators;
+
 # allow init to launch processes in this context
 type hal_evs_driver_exec, exec_type, file_type, system_file_type;
 init_daemon_domain(hal_evs_driver)

בניית יישום התייחסות EVS HAL כתהליך ספק

כאסמכתא, תוכל להחיל את השינויים הבאים על packages/services/Car/evs/Android.mk . הקפד לאשר שכל השינויים המתוארים פועלים עבור היישום שלך.

diff --git a/evs/sampleDriver/Android.mk b/evs/sampleDriver/Android.mk
index 734feea7d..0d257214d 100644
--- a/evs/sampleDriver/Android.mk
+++ b/evs/sampleDriver/Android.mk
@@ -16,7 +16,7 @@ LOCAL_SRC_FILES := \
 LOCAL_SHARED_LIBRARIES := \
     android.hardware.automotive.evs@1.0 \
     libui \
-    libgui \
+    libgui_vendor \
     libEGL \
     libGLESv2 \
     libbase \
@@ -33,6 +33,7 @@ LOCAL_SHARED_LIBRARIES := \
 LOCAL_INIT_RC := android.hardware.automotive.evs@1.0-sample.rc

 LOCAL_MODULE := android.hardware.automotive.evs@1.0-sample
+LOCAL_PROPRIETARY_MODULE := true

 LOCAL_MODULE_TAGS := optional
 LOCAL_STRIP_MODULE := keep_symbols
@@ -40,6 +41,7 @@ LOCAL_STRIP_MODULE := keep_symbols
 LOCAL_CFLAGS += -DLOG_TAG=\"EvsSampleDriver\"
 LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
 LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
+LOCAL_CFLAGS += -Iframeworks/native/include

 # NOTE:  It can be helpful, while debugging, to disable optimizations
 #LOCAL_CFLAGS += -O0 -g
diff --git a/evs/sampleDriver/service.cpp b/evs/sampleDriver/service.cpp
index d8fb31669..5fd029358 100644
--- a/evs/sampleDriver/service.cpp
+++ b/evs/sampleDriver/service.cpp
@@ -21,6 +21,7 @@
 #include <utils/Errors.h>
 #include <utils/StrongPointer.h>
 #include <utils/Log.h>
+#include <binder/ProcessState.h>

 #include "ServiceNames.h"
 #include "EvsEnumerator.h"
@@ -43,6 +44,9 @@ using namespace android;
 int main() {
     ALOGI("EVS Hardware Enumerator service is starting");
+    // Use /dev/binder for SurfaceFlinger
+    ProcessState::initWithDriver("/dev/binder");
+
     // Start a thread to listen video device addition events.
     std::atomic<bool> running { true };
     std::thread ueventHandler(EvsEnumerator::EvsUeventThread, std::ref(running));
diff --git a/evs/sepolicy/evs_driver.te b/evs/sepolicy/evs_driver.te
index f1f31e9fc..632fc7337 100644
--- a/evs/sepolicy/evs_driver.te
+++ b/evs/sepolicy/evs_driver.te
@@ -3,6 +3,9 @@ type hal_evs_driver, domain, coredomain;
 hal_server_domain(hal_evs_driver, hal_evs)
 hal_client_domain(hal_evs_driver, hal_evs)

+# allow to use /dev/binder
+typeattribute hal_evs_driver binder_in_vendor_violators;
+
 # allow init to launch processes in this context
 type hal_evs_driver_exec, exec_type, file_type, system_file_type;
 init_daemon_domain(hal_evs_driver)
@@ -22,3 +25,7 @@ allow hal_evs_driver ion_device:chr_file r_file_perms;

 # Allow the driver to access kobject uevents
 allow hal_evs_driver self:netlink_kobject_uevent_socket create_socket_perms_no_ioctl;
+
+# Allow the driver to use the binder device
+allow hal_evs_driver binder_device:chr_file rw_file_perms;