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

François Beaufort
François Beaufort

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

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

כדאי לבדוק את הדרישות של אפליקציית שיתוף הווידאו כדי להבין איפה 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() מסתיימת בהצלחה, ה-Promise המוחזר נפתר כשאפשר להבטיח שכל הפריימים הבאים של הסרטון יהיו מוגבלים ל-captureTarget.

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

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

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

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

השיטה restrictTo() מוצגת בכל טראק וידאו של צילום כרטיסייה, ולא רק בצילום עצמי. אבל בשלב זה, התכונה 'צילום רכיבים' מופעלת רק לצילום עצמי. לכן, מומלץ לבדוק אם המשתמש בחר בכרטיסייה הנוכחית לפני שמנסים להגביל את הטראק. אפשר לעשות זאת באמצעות Capture Handle. אפשר גם לבקש מהדפדפן לדחוף את המשתמש לצילום עצמי באמצעות 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 Capture, אפשר להריץ את הדמו ב-Glitch. חשוב לבדוק את קוד המקור.

משוב

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

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

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

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

בעיה בהטמעה?

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

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

אימות חתימות

צילום: Paul Skorupskas ב-Unsplash