צילום וידאו בסטרימינג מכל רכיב

François Beaufort
François Beaufort

באמצעות Screen Recording API אפשר לצלם את כל הכרטיסייה הנוכחית. Element Capture API מאפשר לכם לתעד אלמנט HTML ספציפי. היא הופכת צילום של הכרטיסייה כולה לצילום של עץ משנה ספציפי של DOM, שבו מתועדים רק הצאצאים הישירים של רכיב היעד. במילים אחרות, המערכת חותכת ומסירה גם תוכן שמסתיר וגם תוכן שמוסתר.

למה כדאי להשתמש ב-Element Capture?

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

צילום מסך של שיחת וידאו ב-Chrome.
אלד משתמש באפליקציה של צד שלישי בשיחת ועידה בווידאו עם פרנסואה.

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

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

צילום מסך של רשימה נפתחת שמסתירה תוכן שמיועד לצילום.
רשימה נפתחת מופיעה מעל התוכן שרוצים לצלם.

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

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

העובדה שצילום האזור מתעד חלקים מרכיבים בדרך הזו (שנקראת חסימת תוכן) יוצרת כמה בעיות:

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

Element Capture API פותר את כל הבעיות האלה, כי הוא מאפשר לכם לטרגט את הרכיב שרוצים לשתף.

צילום מסך של רכיב היעד ללא רשימת תפריטים נפתחת בתצוגה.
François לא רואה את הרשימה הנפתחת של Elad.

איך משתמשים ב-Element Capture?

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

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

בודקים שוב את השלבים הבאים:

כדי להתחיל, צריך לאפשר למשתמש לצלם את הכרטיסייה הנוכחית.

// Ask the user for permission to start capturing the current tab.
const stream = await navigator.mediaDevices.getDisplayMedia({
 preferCurrentTab: true,
});
const [track] = stream.getVideoTracks();

מגדירים RestrictionTarget על ידי קריאה ל-RestrictionTarget.fromElement() עם רכיב לבחירתכם כקלט.

// Associate captureTarget with a new RestrictionTarget
const captureTarget = document.querySelector("#captureTarget");
const restrictionTarget = await RestrictionTarget.fromElement(captureTarget);

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

// Start restricting the self-capture video track using the RestrictionTarget.
await track.restrictTo(restrictionTarget);

// Enjoy! Transmit remotely.

ירידה לעומק

זיהוי תכונות

כדי לבדוק אם יש תמיכה ב-RestrictionTarget.fromElement(), משתמשים ב-:

if ("RestrictionTarget" in self && "fromElement" in RestrictionTarget) {
  // Deriving a restriction target is supported.
}

הפקת RestrictionTarget

מתמקדים ברכיב שנקרא captureTarget. כדי להפיק ממנו RestrictionTarget, צריך לבצע קריאה ל-RestrictionTarget.fromElement(captureTarget). אם הפעולה תתבצע בהצלחה, ה-Promise שהוחזר יפתור עם אובייקט RestrictionTarget חדש. אחרת, הבקשה תידחה אם יצרתם מספר לא סביר של אובייקטים מסוג RestrictionTarget.

const captureTarget = document.querySelector("#captureTarget");
const restrictionTarget = await RestrictionTarget.fromElement(captureTarget);

בניגוד לאלמנט, אובייקט RestrictionTarget הוא ניתן לסריאליזציה. למשל, אפשר להעביר אותה למסמך אחר באמצעות Window.postMessage().

הגבלה

כשמצלמים כרטיסייה, הטראק של הסרטון חושף את restrictTo(). כשמצלמים את הכרטיסייה הנוכחית, אפשר להפעיל את restrictTo() עם null או עם כל RestrictionTarget שמבוסס על רכיב בכרטיסייה הנוכחית.

קריאות ל-restrictTo(restrictionTarget) משנות את טראק הסרטון לתיעוד של captureTarget, כאילו הוא נמשך בעצמו, ללא תלות בשאר ה-DOM. כל הצאצאים של captureTarget נתפסו גם הם. האחים של captureTarget מסולקים מהצילום. כתוצאה מכך, כל הפריימים שהושגו בטראק נראים כאילו הם נחתכו לקווי המתאר של captureTarget וכל תוכן מסתיר או מוסתר מוסרים.

// Start restricting the self-capture video track using the RestrictionTarget.
await track.restrictTo(restrictionTarget);

קריאות ל-restrictTo(null) מחזירות את הטראק למצב המקורי שלו.

// Stop restricting.
await track.restrictTo(null);

אם הקריאה אל restrictTo() תאושר, ההבטחה שהוחזרה תיפתר כשניתן יהיה להבטיח שכל הפריימים הבאים של הווידאו יוגבלו ל-captureTarget.

אם הפעולה לא מסתיימת בהצלחה, ה-Promise נדחה. שיחה שנכשלה אל restrictTo() נובעת מאחת מהסיבות הבאות:

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

שיקולים לגבי צילום עצמי

כשאפליקציה קוראת ל-getDisplayMedia() והמשתמש בוחר לצלם את הכרטיסייה של האפליקציה, אנחנו קוראים לזה 'צילום עצמי'.

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

שקיפות

מסגרות הווידאו שהאפליקציה מקבלת דרך getDisplayMedia() לא כוללות ערוץ אלפא. אם אפליקציה מגדירה יעד צילום שקוף חלקית, יכולות להיות לכך כמה השלכות אפשריות:

  • הצבעים עשויים להשתנות. רכיבי יעד שקופים חלקית המשורטטים על רקע בהיר עשויים להיראות כהים יותר כשמסירים את ערוץ האלפא, ורכיבים שמצוינים על רקע כהה עשויים להיראות בהירים יותר.
  • צבעים שהיו נסתרים או לא גלויים למשתמש כשערוץ האלפא הוגדר למקסימום, יופיעו לאחר הסרה של ערוץ האלפא. לדוגמה, אם בקטעים השקופים היה קוד RGBA‏ rgba(0, 0, 0, 0), יכול להיות שיופיעו אזורים שחורים לא צפויים בפריימים שצולמו.
צילום מסך של התוצאה של יעד צילום שקוף שאינו מלבן.
שידור הווידאו של יעד הצילום השקוף שאינו מלבני (בצד שמאל) הוא מלבן עם רקע שחור שמכיל עיגול כחול אטום.

יעדים לא כשירים לחיוב מיידי

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

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

<div id="captureTarget" style="isolation: isolate;"></iframe>

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

הפעלת צילום רכיבים

Element Capture API זמין ב-Chrome במחשבים, מאחורי הדגל Element Capture, וניתן להפעיל אותו בכתובת chrome://flags/#element-capture.

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

אבטחה ופרטיות

כדי להבין את הפשרות האבטחה, כדאי לעיין בקטע שיקולים לגבי פרטיות ואבטחה במפרט של Element Capture.

דפדפן Chrome מצייר גבול כחול סביב הקצוות של הכרטיסיות שצולמו.

הדגמה (דמו)

כדי לשחק עם Element Upload (צילום רכיבים), מריצים את ההדגמה ב-Glitch. חשוב לבדוק את קוד המקור.

משוב

צוות Chrome וקהילת תקני האינטרנט רוצים לשמוע על החוויה שלכם עם Element Capture.

נשמח לשמוע על העיצוב

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

  • אפשר לשלוח דיווח על בעיה במפרט במאגר GitHub או להוסיף את המחשבות שלכם לבעיה קיימת.

בעיה בהטמעה?

מצאת באג באופן ההטמעה של Chrome? או שההטמעה שונה מהמפרט?

  • שולחים דיווח על באג בכתובת https://new.crbug.com. חשוב לכלול כמה שיותר פרטים והוראות פשוטות לשחזור הבעיה. Glitch הוא כלי מצוין לשיתוף שחזור מהיר וקל של באגים.

אימות חתימות

צילום: Paul Skorupskas ב-Unsplash