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

פרנסואה בופורט
פרנסואה בופורט
אלעד אלון
אלעד אלון

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

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

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

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

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

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

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

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

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

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

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

Element Capture API פותר את כל הבעיות האלו, בכך שהוא מאפשר לך למקד לרכיב שברצונך לשתף.

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

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

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

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

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

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

שקיפות

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

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

יעדי חילוץ לא כשירים

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

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

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

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

הפעלת לכידת רכיבים

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

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

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

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

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

הדגמה (דמו)

כדי לשחק בתכונה 'צילום רכיבים', מריצים את ההדגמה ב-Glitch. הקפידו לבדוק את קוד המקור.

משוב

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

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

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

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

נתקלת בבעיה בהטמעה?

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

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

אימות חתימות

תמונה מאת Paul Skorupskas ב-Unwash